diff --git a/docs/.gitignore b/docs/.gitignore index d812853..8b3813a 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -3,3 +3,4 @@ _site .jekyll-cache .jekyll-metadata *.af~lock~ +_bak \ No newline at end of file diff --git a/docs/Features/64bit.md b/docs/Features/64bit.md new file mode 100644 index 0000000..4ac5e9b --- /dev/null +++ b/docs/Features/64bit.md @@ -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). diff --git a/docs/Features/Advanced/API-Declarations.md b/docs/Features/Advanced/API-Declarations.md new file mode 100644 index 0000000..fd5a50d --- /dev/null +++ b/docs/Features/Advanced/API-Declarations.md @@ -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` +``` diff --git a/docs/Features/Advanced/Assembly.md b/docs/Features/Advanced/Assembly.md new file mode 100644 index 0000000..718c569 --- /dev/null +++ b/docs/Features/Advanced/Assembly.md @@ -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.) diff --git a/docs/Features/Advanced/Classes-and-Modules.md b/docs/Features/Advanced/Classes-and-Modules.md new file mode 100644 index 0000000..39383bb --- /dev/null +++ b/docs/Features/Advanced/Classes-and-Modules.md @@ -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. diff --git a/docs/Features/Advanced/Multithreading.md b/docs/Features/Advanced/Multithreading.md new file mode 100644 index 0000000..77d63b7 --- /dev/null +++ b/docs/Features/Advanced/Multithreading.md @@ -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. diff --git a/docs/Features/Advanced/Static-Linking.md b/docs/Features/Advanced/Static-Linking.md new file mode 100644 index 0000000..ca6a8cb --- /dev/null +++ b/docs/Features/Advanced/Static-Linking.md @@ -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. diff --git a/docs/Features/Advanced/index.md b/docs/Features/Advanced/index.md new file mode 100644 index 0000000..fbd5143 --- /dev/null +++ b/docs/Features/Advanced/index.md @@ -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 diff --git a/docs/Features/Attributes-Intro.md b/docs/Features/Attributes-Intro.md new file mode 100644 index 0000000..67a2b77 --- /dev/null +++ b/docs/Features/Attributes-Intro.md @@ -0,0 +1,23 @@ +--- +title: Attributes +parent: Features +nav_order: 1 +permalink: /Features/Attributes-Intro +--- + +# Attributes + +twinBASIC supports defining attributes directly in code to annotate modules, classes, types, procedures, and more. These attributes provide compiler instructions and metadata. + +Attributes have two major functions: + +- they can act as instructions to compiler to influence how code is generated, or + +- to annotate Forms, Modules, Classes, Types, Enums, Declares, and [procedures](../tB/Gloss#procedure) i.e. Subs/Functions/Properties. + +Previously in VBx, these attributes, such as procedure description, hidden, default member, and others, were set via hidden text the IDE's editor didn't show you, configured via the Procedure Attributes dialog or some other places. In tB, these are all visible in the code editor. The legacy ones from VBx are supported for compatibility, but new attributes utilize the following syntax: +`[Attribute]` or `[Attribute(value)]` + +Many new attributes enable the powerful additional language features twinBASIC provides, so some of the following items have their associated attributes included in their description. + +See also [the comprehensive reference for attributes](../tB/Core/Attributes). diff --git a/docs/Features/Compiler-IDE/CodeLens.md b/docs/Features/Compiler-IDE/CodeLens.md new file mode 100644 index 0000000..cb8b7db --- /dev/null +++ b/docs/Features/Compiler-IDE/CodeLens.md @@ -0,0 +1,13 @@ +--- +title: CodeLens +parent: Compiler and IDE Features +nav_order: 3 +--- + +# Run Subs from the IDE + +The CodeLens feature allows running Subs and Functions, with no arguments and in modules (but not classes/Forms/UserControls) right from the editor without starting the full program. It has robust access to your code; it can access constants, call other functions both instrinsic and user-define, call APIs, and print to the Debug Console. + +Methods eligible to run with CodeLens (when enabled), have a bar above them that you can click to run: + +![image](../Images/351d0147-cad3-4e16-89e5-0a9e43496740.png) diff --git a/docs/Features/Compiler-IDE/Compiler-Warnings.md b/docs/Features/Compiler-IDE/Compiler-Warnings.md new file mode 100644 index 0000000..136df69 --- /dev/null +++ b/docs/Features/Compiler-IDE/Compiler-Warnings.md @@ -0,0 +1,62 @@ +--- +title: Compiler Warnings +parent: Compiler and IDE Features +nav_order: 1 +permalink: /Features/Compiler-IDE/Compiler-Warnings +--- + +# Compiler Warnings + +twinBASIC provides compiler warnings during design time for common bad practices or likely oversights. + +## Available Warnings + +### Warnings for Likely Incorrect Hex Literals + +Non-explicit values are coerced into the lowest possible type first. So if you declare a constant as `&H8000`, the compiler sees it as an -32,768 `Integer`, and when you're putting that into a `Long` you almost certainly do not want -32,768, you want **positive** 32,768, which requires you to instead use `&H8000&`. + +This warning is supplied for `&H8000`-`&HFFFF` and `&H80000000`-`&HFFFFFFFF`. + +### Warnings for Implicit Variable Creation with ReDim + +When you use `ReDim myArray(1)`, the `myArray` variable is created for you, when it's good practice to declare all variables first. + +### Warnings for Use of DefType + +This feature is discouraged for it making code difficult to read and prone to difficult to debug errors. + +The full list can be found in your project's Settings page: + +![image](../Images/017bd6f8-4b35-43a9-b6be-84cba69daf64.png) + +## Adjusting Warnings + +Each warning has the ability to set them to ignore or turn them into an error both project-wide via the Settings page, and per-module/class, and per-procedure with `[IgnoreWarnings(TB___)]`, `[EnforceWarnings(TB____)]`, and `[EnforceErrors(TB____)]` attributes, where the underscores are replaced with the **full** number, e.g. `[IgnoreWarnings(TB0001)]`; the leading zeroes must be included. + +## Strict Mode + +twinBASIC has added the following warning messages to support something similar to .NET's Strict Mode, where certain implicit conversions are not allowed and must be made explicit. By default, these are all set to be ignored, and must be enabled in the "Compiler Warnings" section of Project Settings or per-module/procedure with `[EnforceWarnings()]`. All of these can be configured individually and ignored for procedure/module scope with `[IgnoreWarnings()]` + +### TB0018: Implicit Narrowing Conversion + +Such as converting a Long to Integer; if you have `Dim i As Integer, l As Long` then `i = l` will trigger the warning, and `i = CInt(l)` would be required to avoid it. + +### TB0019: Implicit Enumeration Conversion + +When assigning a member of one Enum to a variable typed as another, such as `Dim day As VbDayOfWeek: day = vbBlack`. The `CType(Of )` operator whose use in pointers was described in the previous section is also used to specify an explicit type conversion in this case; the warning would not be triggered by `day = CType(Of VbDayOfWeek)(vbBlack)`. + +### TB0020: Suspicious Interface Conversion + +If a declared coclass doesn't explicitly name an interface as supported, converting to it will trigger this warning, e.g.: + +```vb +Dim myPic As StdPicture +Dim myFont As StdFont +Set myFont = myPic +``` + +You'd use `Set myFont = CType(OfStdFont)(myPic)` to avoid this warning. + +### TB0021: Implicit Enumeration Conversion to/from Numeric + +Triggered by assigning a numeric literal to a variable typed as an Enum, such as `Dim day As VbDayOfWeek: day = 1`. To avoid it you'd use `day = CType(Of VbDayOfWeek)(1)`. diff --git a/docs/Features/Compiler-IDE/Debugging.md b/docs/Features/Compiler-IDE/Debugging.md new file mode 100644 index 0000000..4928e66 --- /dev/null +++ b/docs/Features/Compiler-IDE/Debugging.md @@ -0,0 +1,26 @@ +--- +title: Debugging +parent: Compiler and IDE Features +nav_order: 2 +permalink: /Features/Compiler-IDE/Debugging +--- + +# Debugging Features + +twinBASIC includes several features to help with debugging. + +## Debug Trace Logger + +New to the debugging experience is a powerful trace logging feature that can automatically create detailed logs to either the debug console or a file. Messages can be output to the new system with `Debug.TracePrint`. The logger works both when running from the IDE and in compiled executables. + +![image](../Images/4fc2bf99-2bec-4943-837d-21038d791574.png) + +## Stale/Dangling Pointer Detection + +Bugs result from using Strings and Variants after they have been freed. It may not be noticed immediately if the memory has not been overwritten, but it's sometimes hard to detect and can cause issues like a String displaying it's previous value or garbage. This debugging option detects use-after-free, and replaces the data with a special symbol indicating the problem. + +Below shows an example where the ListView ColumnHeader text had been set by previously-freed string and detected by this feature: + +![image](../Images/021f6cbf-acce-445d-ade7-3fcad0af4927.png) + +Previously, it had shown the same text for every column-- but only under certain circumstances, leading to the issue being overlooked for a long time. diff --git a/docs/Features/Compiler-IDE/IDE-Features.md b/docs/Features/Compiler-IDE/IDE-Features.md new file mode 100644 index 0000000..3537542 --- /dev/null +++ b/docs/Features/Compiler-IDE/IDE-Features.md @@ -0,0 +1,66 @@ +--- +title: Modern IDE Features +parent: Compiler and IDE Features +nav_order: 4 +permalink: /Features/Compiler-IDE/IDE-Features +--- + +# Modern IDE Features + +While the twinBASIC IDE still has a lot of work planned, it already includes a number of features that make life much easier found in other modern IDE, but not the ancient VBx IDEs. + +## Theme System + +Fully theme-able, with Dark (default), Light, and Classic (Light) built in, and an easy inheritance-based system to add your own themes via CSS files. + +## Code Navigation and Structure + +- **Code folding**, with foldable custom-defined regions via `#Region "name" ... #End Region` blocks. +- **Sticky-scroll**, which keeps context lines at the top showing major sections of code like module, region, method, `With` blocks, etc. +- **Indent guides**, lines drawn along common indent points to help line things up right. +- **Code mini-map**, shows a graphics overview of the code structure alongside the scroll bar, helping to guide your scrolling. + +## Editing Features + +- **Fully customizable keyboard shortcuts** covering all commands, with ability to save and switch between different sets. +- **Auto-indent on paste**. +- **Paste as comment**. +- **Inline code hints**, which provide annotations at the end of blocks for what the block is (see picture). +- **Color-matching** for parentheses and brackets. + +## Advanced Features + +- **Full Unicode support** in .twin files, so you can use the full Unicode range of the font in your comments and strings. +- **Advanced Information popup**, which shows offsets for UDT members, their total size via both `Len()` plus `LenB()`, and their alignment; and v-table entry offsets for interfaces and classes, as well as their inheritance chain. +- **A type library viewer** for controls and TLB files that displays the full contents in twinBASIC-style syntax rather than ODL. + +## Panels and Windows + +- **A History panel** containing a list of recently modified methods. +- **An Outline panel** with selectable categories. +- **Problems panel**, provides a list of all current errors and warnings (you can filter to show only one or the other). + +## Form Designer Enhancements + +On the Form Designer, control with `Visible = False` are faded to visually indicate this. Also, pressing and holding Control shows the tab index of each tab stop. + +![image](../Images/014a1d28-30af-4a4d-8b9b-83ab6084f00a.png) +[Full size](../Images/fafaloneIDEscreenshot1.png) + +### New Code-Based Project Explorer + +A new code structure based Project Explorer: + +![image](../Images/9a5c50d5-a9f8-44a7-96f7-ae84548bd7ef.png) + +The classic file-based view is still used by default, you can activate the new view with a toggle button: + +![image](../Images/b000d3aa-3689-4d94-88e3-bca44f8b7de6.png) + +## View Forms and Packages as JSON + +Project forms and packages are stored as JSON format data, and you can view this by right-click in Project Explorer and selecting 'View as JSON'. This is particularly interesting for packages as it exposes the entire code in a much more easily parsed format. + +![image](../Images/22660f54-ff5d-4b21-93d3-39715f1f35ed.png) + +![image](../Images/a6525b1d-ac22-4303-ae27-7984c20eba0c.png) diff --git a/docs/Features/Compiler-IDE/Package-Server.md b/docs/Features/Compiler-IDE/Package-Server.md new file mode 100644 index 0000000..26bde56 --- /dev/null +++ b/docs/Features/Compiler-IDE/Package-Server.md @@ -0,0 +1,20 @@ +--- +title: Package Server +parent: Compiler and IDE Features +nav_order: 5 +permalink: /Features/Compiler-IDE/Package-Server +--- + +# Package Server + +Code can be grouped as a package, and published to an online server. You can have Private packages, visible only to you, or Public packages, visible to everyone. + +![image](../Images/5951dab6-738e-4b63-83c4-3331ec6d36b9.png) + +For more information, see the following pages: + +- [What is a package](../Packages/) +- [Creating a TWINPACK package](../Packages/Creating-TWINPACK) +- [Importing a package from a TWINPACK file](../Packages/Importing-TWINPACK) +- [Importing a package from TWINSERV](../Packages/Importing-TWINSERV) +- [Updating a package](../Packages/Updating) diff --git a/docs/Features/Compiler-IDE/index.md b/docs/Features/Compiler-IDE/index.md new file mode 100644 index 0000000..5014977 --- /dev/null +++ b/docs/Features/Compiler-IDE/index.md @@ -0,0 +1,19 @@ +--- +title: Compiler and IDE Features +parent: Features +nav_order: 8 +permalink: /Features/Compiler-IDE/ +has_toc: false +--- + +# Design Experience and Compiler Features + +twinBASIC includes many compiler features and IDE enhancements to improve the development experience. + +## Topics + +- [Compiler Warnings](Compiler-Warnings) - Compiler warnings and strict mode +- [Debugging](Debugging) - Debug trace logger and stale pointer detection +- [CodeLens](CodeLens) - Run Subs from the IDE +- [IDE Features](IDE-Features) - Modern IDE capabilities +- [Package Server](Package-Server) - Package management system diff --git a/docs/Features/Control anchoring and docking.md b/docs/Features/GUI-Components/Anchoring-Docking.md similarity index 84% rename from docs/Features/Control anchoring and docking.md rename to docs/Features/GUI-Components/Anchoring-Docking.md index da4de73..3477e36 100644 --- a/docs/Features/Control anchoring and docking.md +++ b/docs/Features/GUI-Components/Anchoring-Docking.md @@ -1,35 +1,37 @@ --- title: Control Anchoring and Docking -parent: Features +parent: GUI Components nav_order: 2 -permalink: /Features/Anchoring-Docking +permalink: /Features/GUI-Components/Anchoring-Docking +redirect_from: + - /Features/Anchoring-Docking --- # Anchoring One of the new form designer features you see in twinBASIC is the 'Anchors' property: -![image](Images/b26da59b-4e98-40b7-b97b-bb3cef4ca1d0.png) +![image](../Images/b26da59b-4e98-40b7-b97b-bb3cef4ca1d0.png) Clicking the arrow on the left expands it to provide 4 options: -![image](Images/d5dff8f5-c5fa-4620-ba11-430d06276b27.png) +![image](../Images/d5dff8f5-c5fa-4620-ba11-430d06276b27.png) These control whether the position of each point relative to the borders of their parent form or control container are maintained when the form is resized. By default it behaves in the expected manner; the top and left stay the same, and the control is not resized or moved with the form unless you do this manually with code, typically in the `Form_Resize` event. These provide an alternative to handle sizing and moving automatically. If a control is anchored at all 4 positions, it will be resized in both dimensions along with the form: -![image](Images/fddbffa9-2b71-47f5-b925-e67fc66b9e5c.png) +![image](../Images/fddbffa9-2b71-47f5-b925-e67fc66b9e5c.png) As you can see, all anchor points were kept a constant distant from the edge, resulting in the control being resized. If you anchored only the Top, Left, and Bottom, it will be resized vertically, but not horizontally: -![image](Images/3fa1cf2b-0af5-44ae-ae6a-3c0662f51f57.png) +![image](../Images/3fa1cf2b-0af5-44ae-ae6a-3c0662f51f57.png) Right was not anchored to the edge, so it didn't move with the edge. If you remove the anchors to the Top and Left (False) but maintain the Right and Bottom anchors (True), the control will move with the Bottom and Right: -![image](Images/0aeb25f6-d864-4ebb-a9f5-bbd7b5d242e8.png) +![image](../Images/0aeb25f6-d864-4ebb-a9f5-bbd7b5d242e8.png) The control stayed the same size, and because the Right and Bottom were anchored to the edge, they moved with the form, resulting in the whole control moving. @@ -39,11 +41,11 @@ The above examples illustrate how this works with controls directly on a Form. B For example, if a TextBox is anchored at all 4 points, inside a Frame anchored at all 4 points, then it will resize along with the Frame: -![image](Images/4829696d-788b-40ee-bebd-5afa44477460.png) +![image](../Images/4829696d-788b-40ee-bebd-5afa44477460.png) If we remove the Bottom anchor from the TextBox, but not the Frame, the Frame will be resize along the bottom, but the TextBox will not: -![image](Images/bc9f3756-a14b-4ee7-b819-6822497b640a.png) +![image](../Images/bc9f3756-a14b-4ee7-b819-6822497b640a.png) Using these 4 points you can automatically maintain a relative size, position, or both, without having to manually code any of it. @@ -55,11 +57,11 @@ Using these 4 points you can automatically maintain a relative size, position, o Similar to anchoring but slightly different, tB also offers a 'Dock' property: -![image](Images/4c8b881e-1216-4819-a558-d2ce20f47fcd.png) +![image](../Images/4c8b881e-1216-4819-a558-d2ce20f47fcd.png) You might already be familiar with how a StatusBar control locks itself to the bottom of a form; that's the kind of positioning this property controls. A control can be docked on any side, and it will stay sized to the full width or height, and move with, that side of the Form or parent container. For example, a CommandButton with `vbDockBottom`: -![image](Images/599a66ad-31d5-449f-bbf5-00963fe9aa2a.png) +![image](../Images/599a66ad-31d5-449f-bbf5-00963fe9aa2a.png) Besides the four sides, there's a final option: `vbDockFill`. This will have the control fill its entire parent area. This is most useful when used with a container such as a PictureBox or Frame control-- it will fill only that container when its a child of it, not the whole form. @@ -69,7 +71,7 @@ Besides the four sides, there's a final option: `vbDockFill`. This will have the As the end of the last section suggests, it's possible to dock more than one control to the same location, such as a CommandButton and TextBox docked to the bottom. The following example also shows a PictureBox control with the `vbDockRight` + `vbDockFill` example from above: -![image](Images/80185a8d-2952-415f-bc02-ec3ddea89568.png) +![image](../Images/80185a8d-2952-415f-bc02-ec3ddea89568.png) > [!TIP] > The order of two (or more) controls docked in the same position is determined by which was set first. Currently they can't be dragged to rearranged, but you can set the Dock property back to none, and re-do them in the desired order. diff --git a/docs/Features/GUI-Components/Control-Properties.md b/docs/Features/GUI-Components/Control-Properties.md new file mode 100644 index 0000000..54c348f --- /dev/null +++ b/docs/Features/GUI-Components/Control-Properties.md @@ -0,0 +1,24 @@ +--- +title: Control Property Enhancements +parent: GUI Components +nav_order: 6 +permalink: /Features/GUI-Components/Control-Properties +--- + +# Control Property Enhancements + +## TextBox Enhancements + +- `TextBox.NumbersOnly` property: Restricts input to 0-9 by setting the `ES_NUMBER` style on the underlying control. +- `TextBox.TextHint` property: Sets the light gray hint text in an empty TextBox (`EM_SETCUEBANNER`). + +## Label Enhancements + +- `Label.VerticalAlignment` property: Defaults to Top. +- `Label.LineSpacing` property (in twips, default is 0) +- `Label.Angle` property (in degrees, rotates the label text) +- `Label.BorderCustom` property (has suboptions to set size, padding and color of borders independently for each side). + +## Timer Enhancements + +`Timer.Interval` can now be set to any positive `Long` instead of being limited to 65,535. diff --git a/docs/Features/GUI-Components/Forms.md b/docs/Features/GUI-Components/Forms.md new file mode 100644 index 0000000..2356ac8 --- /dev/null +++ b/docs/Features/GUI-Components/Forms.md @@ -0,0 +1,52 @@ +--- +title: Forms +parent: GUI Components +nav_order: 1 +permalink: /Features/GUI-Components/Forms +--- + +# Form Features + +twinBASIC provides numerous enhancements to forms and form handling. + +## Modern Image Format Support + +You no longer face an incredibly limited format selection for images in tB Forms and Controls; not only do the Bitmap and Icon formats support the full range of formats for those, you can additionally load PNG Images, JPEG Images, Metafiles (.emf/.wmf), and SVG Vector Graphics (.svg). + +### Improved LoadPicture + +Additionally, `LoadPicture` can load all image types directly from a byte array, rather than requiring a file on disk. You can use this to load images from resource files or other sources. Note that if your projects references stdole2.tlb (most do), currently you must qualify it as `Global.LoadPicture` to get tB's custom binding that supports byte arrays. + +## Transparency and Alpha Blending + +### Form.TransparencyKey + +This new property specifies a color that will be transparent to the window below it in the z-order (all windows, not just in your project). Setting this property will cause the specified color to be 100% transparent. A Shape control with a solid `FillStyle` is a helpful tool to color the areas of the form in the key color. + +### Form.Opacity + +This sets an alpha blending level for the entire form. Like transparency, this is to all windows immediately underneath it. Note that any areas covered by the `TransparencyKey` color will remain 100% transparent. + +The following image shows a Form with a `TransparencyKey` of Red, using a Shape control to define the transparent area, while also specifying 75% `Opacity` for the entire form: + +![image](../Images/85f25aa2-abc8-4d42-8510-078f8ee4a324.png) + +## Additional Form Features + +In addition to the above, forms have: + +- `DpiScaleX`/`DpiScaleY` properties to retrieve the current values +- `.MinWidth`, `.MinHeight`, `.MaxWidth`, and `.MaxHeight` properties so subclassing isn't needed for this +- `Form.TopMost` property. +- Control anchoring: control x/y/cx/cy can made relative, so they're automatically moved/resized with the Form. For example if you put a TextBox in the bottom right, then check the Right and Bottom anchors (in addition to Top and Left), the bottom right will size with the form on resize. This saves a lot of boiler-plate sizing code. +- Control docking: Controls can be fixed along one of the sides of the Form (or container), or made to fill the whole Form/container. Multiple controls can be combined and mixed/matched in docking positions. + +For more information on Control Anchoring and Control Docking, see the [Anchoring and Docking page](Anchoring-Docking.md). + +## High Quality Scaling in Image Controls + +Image controls now offer a `StretchMode` property that allows you to choose Bilinear, Bicubic, Lanczos3 and Lanczos8 stretching algorithms, which are far superior to the default stretching algorithm. These use built in algorithms so do not add additional dependencies or API calls. + +## DPI Scaling + +PictureDpiScaling property for forms, usercontrols and pictureboxes: PictureDpiScaling property allows you to turn off DPI scaling of images so that they display at 1:1 rather than allowing the OS to stretch them. The idea being you may want to choose a different bitmap manually, rather than apply the somewhat limited OS-stretching. diff --git a/docs/Features/GUI-Components/Modernization.md b/docs/Features/GUI-Components/Modernization.md new file mode 100644 index 0000000..1c05be6 --- /dev/null +++ b/docs/Features/GUI-Components/Modernization.md @@ -0,0 +1,22 @@ +--- +title: Control Modernization +parent: GUI Components +nav_order: 4 +permalink: /Features/GUI-Components/Modernization +--- + +# Control Modernization + +tB will eventually replace all built in controls that you're used to, for now the ones available are: CommandButton, TextBox, ComboBox, CheckBox, OptionButton, Label, Frame, PictureBox, Line, Shape, VScrollBar, HScrollBar, Timer, DriveListBox, DirListBox, FileListBox, Image, and Data from the basic set; then, ListView, TreeView, ProgressBar, DTPicker, MonthView, Slider, and UpDown from the Common Controls. + +## Key Modernization Features + +- **64-bit Support**: Every control can be compiled both as 32bit and 64bit without changing anything. +- **DPI Aware**: They will automatically size correctly when dpi awareness is enabled for your app. +- **Visual Styles**: Controls support Visual Styles per-control. Comctl6 styles can be applied, or not, on a control-by-control basis with the `.VisualStyles` property. + +## Alternatives for Unimplemented Controls + +The best option is Krool's VBCCR and VBFlexGrid projects. These are now available [from the Package Server](../../Packages/Importing-TWINSERV/) in x64-compatible form, and are also DPI aware and support Visual Styles. + +Additionally, the original OCX controls provided by Microsoft will work fine; however, they're mostly 32-bit only. The x64 version of `MSComCtl.ocx` doesn't come with Windows and isn't legally redistributable but if you have Office 64bit, it works in tB. diff --git a/docs/Features/GUI-Components/New.md b/docs/Features/GUI-Components/New.md new file mode 100644 index 0000000..37ae31d --- /dev/null +++ b/docs/Features/GUI-Components/New.md @@ -0,0 +1,30 @@ +--- +title: New Controls +parent: GUI Components +nav_order: 5 +permalink: /Features/GUI-Components/New +--- + +# New Controls + +twinBASIC introduces several new controls to enhance your applications. + +## QR Code Control + +![image](../Images/54ed49d8-b434-45e3-9e63-a1fe75cdf814.png) + +Easily display custom QR codes with a native control. + +## Multiframe Control + +![image](../Images/4ad9c774-b31d-47d3-9963-6d99ac4f37bb.png) + +This control allows you to create a number of frames within it with their size specified as a percentage, such that as the control is resized the frames within expand proportionally. For details and a video demonstration, Mike Wolfe's twinBASIC Weekly Update [covered it when released](https://nolongerset.com/twinbasic-update-april-29-2025/#experimental-multi-frame-control). + +Combined with anchors and docking, this allows designing highly functional and complex layouts visually, without writing any code to handling resizing. + +## CheckMark Control + +![image](../Images/5fc60b7b-4f54-445c-8504-451019b7ec55.png) + +Primarily intended for reports but available in Forms and UserControls as well, the CheckMark control provides a scalable check component where this is fixed to a single size in a normal CheckBox control. diff --git a/docs/Features/GUI-Components/UserControl.md b/docs/Features/GUI-Components/UserControl.md new file mode 100644 index 0000000..f0d8b4d --- /dev/null +++ b/docs/Features/GUI-Components/UserControl.md @@ -0,0 +1,20 @@ +--- +title: UserControl Enhancements +parent: GUI Components +nav_order: 7 +permalink: /Features/GUI-Components/UserControl +--- + +# UserControl Enhancements + +The UserControl object now provides new features for better control handling. + +## PreKeyEvents Property + +The UserControl object now provides the new Boolean property `PreKeyEvents` that enables corresponding new events `PreKeyDown` and `PreKeyUp`. These allow handling special keys like tab, arrows, etc without OS or COM hooks (for example, based on the `IOleInPlaceActiveObject` interface). + +These work with all child windows inside the UserControl, including ones created by `CreateWindowEx`. + +## Access to Raw Message Data + +You can access raw message data in the `PreKeyDown`/`PreKeyUp` event handlers with the new `PreKeyWParam`/`PreKeyLParam` and `PreKeyTargetHwnd` UserControl properties. diff --git a/docs/Features/Windowless vs windowed controls.md b/docs/Features/GUI-Components/Windowless.md similarity index 98% rename from docs/Features/Windowless vs windowed controls.md rename to docs/Features/GUI-Components/Windowless.md index 9a7cbab..41d3832 100644 --- a/docs/Features/Windowless vs windowed controls.md +++ b/docs/Features/GUI-Components/Windowless.md @@ -1,8 +1,10 @@ --- title: Windowless Controls vs. Windowed Controls -parent: Features +parent: GUI Components nav_order: 3 -permalink: /Features/Windowless +permalink: /Features/GUI-Components/Windowless +redirect-from: + - /Features/Windowless --- # Windowless Controls vs. Normal (Windowed) Controls diff --git a/docs/Features/GUI-Components/index.md b/docs/Features/GUI-Components/index.md new file mode 100644 index 0000000..4497083 --- /dev/null +++ b/docs/Features/GUI-Components/index.md @@ -0,0 +1,22 @@ +--- +title: GUI Components +parent: Features +nav_order: 5 +permalink: /Features/GUI-Components/ +has_toc: false +--- + +# GUI Components + +twinBASIC modernizes GUI components with enhanced forms, improved controls, and new control types. + +## Topics + +- [Forms](Forms) - Form enhancements and features +- [Anchoring and Docking](Anchoring-Docking) - Automatic size and position management +- [Windowless Controls](Windowless) - Windowless vs windowed controls +- [Modern Controls](Modernization) - Control modernization and 64-bit support +- [New Controls](New) - QR Code, Multiframe, and CheckMark controls +- [Control Properties](Control-Properties) - Additional control properties and enhancements +- [UserControl Enhancements](UserControl) - UserControl improvements + diff --git a/docs/Features/Language/Alias-Types.md b/docs/Features/Language/Alias-Types.md new file mode 100644 index 0000000..fc26fbe --- /dev/null +++ b/docs/Features/Language/Alias-Types.md @@ -0,0 +1,35 @@ +--- +title: Alias Types +parent: Language Syntax +nav_order: 0 +permalink: /Features/Language/Alias-Types +--- + +# Alias Types + +An alias is an alternative name for a User-Defined Type, intrinsic type, or interface. This is similar to C/C++'s `typedef` statement. These can then be used in place of the original type and will be treated as if the original was used (would not be a type mismatch). + +`[Public|Private] Alias AltName As OrigName` + +### Example + +With intrinsic types, or if you have a type such as: + +```vb +Public Type POINT + x As Long + y As Long +End Type +``` + +You can create aliases: + +```vb +Public Alias POINTAPI As POINT + +Public Alias CBoolean As Byte + +Public Alias KAFFINITY As LongPtr +``` + +Like interfaces and coclasses, these must be placed in a .twin file, outside of `Module` and `Class` blocks. You can create aliases of other aliases. The optional `Public` and `Private` modifiers determine whether the alias is exported to the Type Library of an ActiveX DLL or Control. A `Private` alias would result in usage of it being replaced with the original type. diff --git a/docs/Features/Language/Comments.md b/docs/Features/Language/Comments.md new file mode 100644 index 0000000..ce17ca9 --- /dev/null +++ b/docs/Features/Language/Comments.md @@ -0,0 +1,20 @@ +--- +title: Comment Syntax +parent: Language Syntax +nav_order: 17 +permalink: /Features/Language/Comments +--- + +# New Comment Syntax + +## Block and Inline Comments + +You can now use `/* */` syntax. For example, `Sub Foo(bar As Long /* out */)` or: + +```c +/* +Everything here is +a comment until: +*/ +``` + diff --git a/docs/Features/Language/Data-Types.md b/docs/Features/Language/Data-Types.md new file mode 100644 index 0000000..07fe226 --- /dev/null +++ b/docs/Features/Language/Data-Types.md @@ -0,0 +1,29 @@ +--- +title: Data Types +parent: Language Syntax +nav_order: 1 +permalink: /Features/Language/Data-Types +--- + +# New Data Types + +twinBASIC introduces several new data types to enhance your programming capabilities. + +## LongPtr + +Meant primarily to handle pointers, `LongPtr` is a 4-byte (32 bits) signed integer in 32bit mode, and a signed 8-byte integer (64 bits) in 64bit mode. + +## LongLong + +A signed 8-byte (64 bits) integer, ranging from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. Note that this type is available in both 32bit and 64bit mode (VBA restricts it to 64bit mode). + +## Decimal + +In twinBASIC, `Decimal` is implemented as a full, regular data type, in addition to use within a `Variant`. This is a 16-byte (128 bits) type which holds a 12-byte (96 bits) integer with variable decimal point scaling and sign bit information. Values range from -79,228,162,514,264,337,593,543,950,335 to 79,228,162,514,264,337,593,543,950,335. + +## Type Support + +All of the datatype management features also exist for these types: +- `DefDec`/`DefLngLng`/`DefLongPtr` - Default type declarations +- `CDec`/`CLngLng`/`CLongPtr` - Type conversion functions +- `vbDecimal`/`vbLongLong`/`vbLongPtr` - Type check constants diff --git a/docs/Features/Language/Delegates.md b/docs/Features/Language/Delegates.md new file mode 100644 index 0000000..b585c89 --- /dev/null +++ b/docs/Features/Language/Delegates.md @@ -0,0 +1,60 @@ +--- +title: Delegate Types +parent: Language Syntax +nav_order: 4 +permalink: /Features/Language/Delegates +--- + +# Delegate Types for Indirect Calls + +There is native support for calling a function by pointer, by way of `Delegate` syntax. A delegate in twinBASIC is a function pointer type that's compatible with LongPtr. `AddressOf` returns a delegate type, that's also backwards compatible with `LongPtr`. + +## Basic Usage + +The syntax looks like this: + +```vb +Private Delegate Function Delegate1 (ByVal A As Long, ByVal B As Long) As Long + +Private Sub Command1_Click() + Dim myDelegate As Delegate1 = AddressOf Addition + MsgBox "Answer: " & myDelegate(5, 6) +End Sub + +Public Function Addition(ByVal A As Long, ByVal B As Long) As Long + Return A + B +End Function +``` + +## Advanced Usage + +The delegate type can also be used in interface/API declarations and as members of a User-defined type. For example, the `ChooseColor` API: + +```vb +Public Delegate Function CCHookProc (ByVal hwnd As LongPtr, ByVal uMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr + +Public Type CHOOSECOLOR + lStructSize As Long + hwndOwner As LongPtr + hInstance As LongPtr + rgbResult As Long + lpCustColors As LongPtr + Flags As ChooseColorFlags + lCustData As LongPtr + lpfnHook As CCHookProc 'Delegate function pointer type instead of LongPtr + lpTemplateName As LongPtr +End Type +``` + +If you already have code assigning a `Long`/`LongPtr` to the `lpfnHook` member, it will continue to work normally, but now you can also have the type safety benefits of setting it to a method matching the Delegate: + +```vb +Dim tCC As CHOOSECOLOR +tCC.lpfnHook = AddressOf ChooseColorHookProc + +'... + +Public Function ChooseColorHookProc(ByVal hwnd As LongPtr, ByVal uMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr + +End Function +``` diff --git a/docs/Features/Language/Generics.md b/docs/Features/Language/Generics.md new file mode 100644 index 0000000..52c83fd --- /dev/null +++ b/docs/Features/Language/Generics.md @@ -0,0 +1,52 @@ +--- +title: Generics +parent: Language Syntax +nav_order: 5 +permalink: /Features/Language/Generics +--- + +# Generics + +Generics have basic support in methods and classes. + +## Generic Functions + +```vb +Public Function TCast(Of T)(ByRef Expression As T) As T + Return Expression +End Function +``` + +This could be used e.g. to return a `Date` typed variable with `TCast(Of Date)("2021-01-01")` + +## Generic Classes + +A Class generic allows the type in methods throughout the class. The following example shows this to make a generic List class: + +``` +[COMCreatable(False)] +Class List(Of T) + Private src() As T + Private c As Long + Sub New(p() As T) + src = p + End Sub + [DefaultMember] + Function GetAt(ByVal idx As Long) As T + Return src(idx) + End Function + Public Property Get Count() As Long + Return c + End Property +End Class +``` + +## Usage Example + +```vb +Private Sub TestListGenericClass() + Dim names As List(Of String) = New List(Of String)(Array("John", "Smith", "Kane", "Tessa", "Yoland", "Royce", "Samuel")) + Dim s As String = names(0) + Debug.Print s +End Sub +``` diff --git a/docs/Features/Language/Handlers.md b/docs/Features/Language/Handlers.md new file mode 100644 index 0000000..f873b52 --- /dev/null +++ b/docs/Features/Language/Handlers.md @@ -0,0 +1,21 @@ +--- +title: Handler Method Syntax +parent: Language Syntax +nav_order: 15 +permalink: /Features/Language/Handlers +--- + +# New Handler Class Member Syntax + +You can now separate the name of method from the class member it applies to. + +## Handles for Events + +For events on Forms, UserControls, and event-raising objects, you can define any method as the handler, rather than need to name it as `Object_Event()`, by following it with `Handles Object.Event`. For example, in a form, instead of `Private Sub Form_Load()` you could handle the `Load` event with `Private Sub OnLoad() Handles Form.Load`. + +## Implements for Interfaces + +Similar to the above, for forms/UCs/classes that use `Implements`, you can use `Sub Bar() Implements IFoo.Bar`. Note that you can specify more than one implemented method; for more information, see the [Enhancements to Implements section](Interfaces-CoClasses). + +> [!NOTE] +> These are opt-in and optional. For compatibility, twinBASIC will always continue to support the traditional syntax for event handling and Implements, and you're not required to use this new syntax (or *any* of the additions described in this article). Whether or not automatically created prototypes use this syntax is controlled via IDE Options: "IDE: Use new handles/implements syntax". diff --git a/docs/Features/Language/Inheritance.md b/docs/Features/Language/Inheritance.md new file mode 100644 index 0000000..9fd1040 --- /dev/null +++ b/docs/Features/Language/Inheritance.md @@ -0,0 +1,157 @@ +--- +title: Inheritance +parent: Language Syntax +nav_order: 3 +permalink: /Features/Language/Inheritance +--- + +# Inheritance + +twinBASIC provides several mechanisms for inheritance to support both simple and complete object-oriented programming patterns: **Implements**, **Implements Via** and **Inherits**. + +## Enhancements to **Implements** + +`Implements` in twinBASIC has several enhancements: + +### Inherited Interfaces + +`Implements` in twinBASIC is allowed on inherited interfaces -- for instance, if you have `Interface IFoo2 Extends IFoo`, you then use `Implements IFoo2` in a class, where in VBx this would not be allowed. You'll need to provide methods for all inherited interfaces (besides `IDispatch` and `IUnknown`). The class will mark all interfaces as available-- you don't need a separate statement for `IFoo`, it will be passed through `Set` statements (and their underlying `QueryInterface` calls) automatically. + +### Multiple Implementations + +If you have an interface that multiple others extend from, you can write multiple implementations, or specify one implementation for all. For example: + +```vb +IOleWindow_GetWindow() As LongPtr _ + Implements IOleWindow.GetWindow, IShellBrowser.GetWindow, IShellView2.GetWindow +``` + +### 'As Any' Parameters in Interfaces + +`Implements` is allowed on interfaces with 'As Any' parameters: In VBx, you'd get an error if you attempted to use any interface containing a member with an `As Any` argument. With twinBASIC, this is allowed if you substitute `As LongPtr` for `As Any`, for example: + +```vb +Interface IFoo Extends IUnknown + Sub Bar(ppv As Any) +End Interface + +Class MyClass + Implements IFoo + + Private Sub IFoo_Bar(ppv As LongPtr) Implements IFoo.Bar + + End Sub +``` + +## **Implements Via** for Basic Inheritance + +tB allows simple inheritance among classes. For example, if you have class cVehicle which implements IVehicle containing method Honk, you could create child classes like cCar or cTruck, which inherit the methods of the original, so you could call cCar.Honk without writing a separate implementation. + +![image](../Images/b0724fe2-636d-47db-a8fc-531a585ddaf9.png) + +You can see that the Honk method is only implemented by the parent class, then called from the child class when you click the CodeLens button to run the sub in place from the IDE. + +## **Inherits** for Complete OOP + +This is a more robust option for full inheritance and OOP. It supports `Protected` methods and variables that are accessible to derived classes but not outside callers, `Overridable` and `Overrides` syntax, multiple inheritance, and explicit base class constructors. + +### Example: Animal Class Hierarchy + +Starting with a base class: + +```vb +Private Class Animal + Protected _name As String + Protected _dob As Date ' date of birth + + Public Event Spoke(ByVal sound As String) + + Public Sub New(name As String, dob As Date) + _name = name + _dob = dob + End Sub + + Public Property Get Name() As String + Name = _name + End Property + + Public Property Get DOB() As Date + DOB = _dob + End Property + + ' Age in whole years based on DOB and today's date + Public Function AgeYears() As Long + Dim y As Long + y = DateDiff("yyyy", _dob, Date) + If DateSerial(Year(Date), Month(_dob), Day(_dob)) > Date Then y = y - 1 + AgeYears = y + End Function + + Public Sub Speak() + Dim s As String + s = GetSound() + RaiseEvent Spoke(s) + Debug.Print _name & " says: " & s + End Sub + + ' --- Overridable hook for derived classes --- + Protected Overridable Function GetSound() As String + GetSound = "" + End Function +End Class +``` + +Others can inherit: + +```vb +' ===== Derived: Dog ===== +Private Class Dog + Inherits Animal + + Protected _breed As String + + Public Sub New(name As String, dob As Date, breed As String) + Animal.New(name, dob) ' we can explicitly call base constructors from within our constructor + _breed = breed + End Sub + + Public Property Get Breed() As String + Breed = _breed + End Property + + ' Override: + Protected Overridable Function GetSound() As String Overrides Animal.GetSound + GetSound = "woof" + End Function +End Class + +' ===== Further derived: GuardDog (Dog → GuardDog) ===== +Private Class GuardDog + Inherits Dog + + Protected _onDuty As Boolean + + Public Sub New(name As String, dob As Date, breed As String) + Dog.New(name, dob, breed) ' we can explicitly call base constructors from within our constructor + _onDuty = True + End Sub + + Public Property Get OnDuty() As Boolean + OnDuty = _onDuty + End Property + Public Property Let OnDuty(ByVal v As Boolean) + _onDuty = v + End Property + + ' Multi-level override (overriding Dog's override): + Protected Function GetSound() As String Overrides Dog.GetSound + If _onDuty Then + GetSound = "WOOF!" + Else + GetSound = "woof" + End If + End Function +End Class +``` + +This is just an excerpt, see the full Sample 23 for additional classes, usage, and information about inheritance in twinBASIC. diff --git a/docs/Features/Language/Inline-Initialization.md b/docs/Features/Language/Inline-Initialization.md new file mode 100644 index 0000000..b5bc785 --- /dev/null +++ b/docs/Features/Language/Inline-Initialization.md @@ -0,0 +1,32 @@ +--- +title: Inline Variable Initialization +parent: Language Syntax +nav_order: 14 +permalink: /Features/Language/Inline-Initialization +--- + +# Inline Variable Initialization + +You can now set initial values for variables inline, without needing a line-continuation character. + +## Examples + +```vb +Dim i As Long = 1 +Dim foo As Boolean = bar() +Dim arr As Variant = Array(1, 2, 3) +Dim strArr(2) As String = Array("a", "b", "c") +Dim cMC As cMyClass = New cMyClass(customConstructorArgs) +``` + +## Inline Variable Declaration for For + +You now no longer need a separate `Dim` statement for counter variables: + +```vb +For i As Long = 0 To 10 + '... +Next +``` + +is now valid syntax. You can use any type, not just `Long`. diff --git a/docs/Features/Language/Interfaces-CoClasses.md b/docs/Features/Language/Interfaces-CoClasses.md new file mode 100644 index 0000000..66cce8b --- /dev/null +++ b/docs/Features/Language/Interfaces-CoClasses.md @@ -0,0 +1,151 @@ +--- +title: Interfaces and CoClasses +parent: Language Syntax +nav_order: 2 +permalink: /Features/Language/Interfaces-CoClasses +--- + +# Interfaces, CoClasses, and Aliases + +twinBASIC supports these features as native language syntax where in VBx they were only supported via Type Libraries. + +## Defining Interfaces + +twinBASIC supports defining COM interfaces using BASIC syntax, rather than needing an type library with IDL and C++. These are only supported in .twin files, not in legacy .bas or .cls files. They must appear *before* the `Class` or `Module` statement, and will always have a project-wide scope. The generic form for this is as follows: + +``` +[InterfaceId ("00000000-0000-0000-0000-000000000000")] +** +Interface Extends + ** + + ** + + ... +End Interface +``` + +Methods can be any of the following: `Sub`, `Function`, `Property Get`, `Property Let`, or `Property Set`, with arguments following the standard syntax, and with the standard attributes available. These cannot be modified with `Public/Private/Friend`. `End ` is not used, as these are prototype definitions only. + +### Available Attributes for Interfaces + +- `[Description("text")]` - Provides a description in information popups, and is exported as a `helpstring` attribute in the type library (if applicable). +- `[Hidden]` - Hides the interface from certain Intellisense and other lists. +- `[Restricted]` - Restricts the interface methods from being called in most contexts. +- `[OleAutomation(True/False)]` - Controls whether this attribute is applied in the typelibrary. This attribute is set to **True** by default. +- `[ComImport]` - Specifies that an interface is an import from an external COM library, for instance, the Windows shell. +- `[ComExtensible(True/False)]` - Specifies whether new members added at runtime can be called by name through an interface implementing IDispatch. This attribute is set to **False** by default. + +### Available Attributes for Methods + +- `[Description("text")]` - Provides a description +- `[PreserveSig]` - For COM interfaces, normally methods return an HRESULT that the language hides from you. The `[PreserveSig]` attribute overrides this behavior and defines the function exactly as you provide. This is necessary if you need to define it as returning something other than a 4-byte `Long`, or want to handle the result yourself, bypassing the normal runtime error raised if the return value is negative (this is helpful when a negative value indicates an expected, acceptable failure, rather than a true error, like when an enum interface is out of items). +- `[DispId(number)]` - Defines a dispatch ID associated with the method. + +### Example + +``` +[InterfaceId("E7064791-0E4A-425B-8C8F-08802AAFEE61")] +[Description("Defines the IFoo interface")] +[OleAutomation(False)] +Interface IFoo Extends IUnknown + Sub MySub(Arg1 As Long) + Function Clone() As IFoo + [PreserveSig] + Function MyFunc([TypeHint(MyEnum)] Arg1 As Variant) As Boolean +End Interface +``` +(Where MyEnum is a standard `Enum ... End Enum` block.) + +## Defining CoClasses + +In addition to interfaces, twinBASIC also allows defining coclasses -- creatable classes that implement one or more defined interfaces. Like interfaces, these too must be in .twin files and not legacy .bas/.cls files, and must appear prior to the `Class` or `Module` statement. The generic form is: + +``` +[CoClassId("00000000-0000-0000-0000-000000000000")] +** +CoClass + [Default] Interface + *[Default, Source] Interface * + ** +End CoClass +``` + +Each coclass must specify at least one interface but may have several more. It can optionally mark an interface as default or a source. It is typical and highly recommended that an interface be marked with `[Default]` attribute and in cases where it has events to also specify `[Default, Source]` to indicate the default interface used for events. Each represents a contract that the class will provide an implementation of the given interface. Note that at this time, twinBASIC does not yet support defining `dispinterface` interfaces (aka, dispatch-only interface) the usual form of source interfaces for events. + +### Attributes for Coclasses + +- `[Description("text")]` - Provides a description in info popups and other places. +- `[ComCreatable(True/False)]` - Indicates that this coclass can be created with the `New` keyword. This is *True* by default. +- `[AppObject]` - Indicates the class is part of the global namespace. You should not include this attribute without a full understanding of the meaning. +- `[Hidden]` - Hides the coclass from appearing in certain places. +- `[CoClassCustomConstructor("fully qualified path to factory method")]` - Allows custom logic for creating and returning a new instance of the coclass' implementation. + +### Example + +``` +[CoClassId("52112FA1-FBE4-11CA-B5DD-0020AFE7292D")] +CoClass Foo + [Default] Interface IFoo + Interface IBar +End CoClass +``` +Where `IFoo` and `IBar` are interfaces defined with the `Interface` syntax described earlier. + +## Custom Constructor Example + +```vb +[InterfaceId("016BC30A-A8E0-4AAF-93AE-13BD838A149E")] +Public Interface IFoo + Sub Foo() +End Interface + +[InterfaceId("2A20E655-30A4-4534-86BC-6A7E281C425D")] +Public Interface IBar + Sub Bar() +End Interface + +[CoClassId("7980D953-10BF-478C-93BB-DD0093315D96")] +[CoClassCustomConstructor("FooFactory.CreateFoo")] +[COMCreatable(True)] +Public CoClass Foo + [Default] Interface IFoo + Interface IBar +End CoClass + +' The implementation do not have to be exposed. The coclass is a sufficient description +' and we should implement the interfaces that the coclass exposes. +Private Class FooImpl + Implements IFoo + Implements IBar + + Public Sub Foo() Implements IFoo.Foo + Debug.Print "Foo ran" + End Sub + + Public Sub Bar() Implements IBar.Bar + Debug.Print "Bar ran" + End Sub +End Class + +Public Module FooFactory + ' The signature must be "preserved", returning a HRESULT + ' and the new instance via the "out" parameter. + ' Note that we new up the FooImpl but return the Foo coclass. + Public Function CreateFoo(ByRef RHS As Foo) As Long + Set RHS = New FooImpl + Return 0 ' S_OK + End Function +End Module + +Public Module Test + Public Sub DoIt() + Dim MyFoo As Foo + ' create a new instance of coclass Foo + ' this implicilty calls the custom constructor + ' in the FooFactory. + Set MyFoo = New Foo + MyFoo.Foo + End Sub +End Module +``` diff --git a/docs/Features/Language/Literals.md b/docs/Features/Language/Literals.md new file mode 100644 index 0000000..942b5bf --- /dev/null +++ b/docs/Features/Language/Literals.md @@ -0,0 +1,18 @@ +--- +title: Literals +parent: Language Syntax +nav_order: 8 +permalink: /Features/Language/Literals +--- + +# New Literals Notation + +twinBASIC provides new options for writing numeric literals. + +## Binary Literals + +In addition to `&H` for hexadecimal literals and `&O` for octal notation, twinBASIC also provides `&B` for binary notation. For example, `Dim b As Long = &B010110` is valid syntax, and b = 22. + +## Digit Grouping + +The `&H`, `&O`, and `&B` literals can all be grouped using an underscore, for example, grouping a `Long` by it's constituent binary byte groups: `&B10110101_10100011_10000011_01101110`, or grouping a `LongLong` as two `Long` groups: `&H01234567_89ABCDEF`. diff --git a/docs/Features/Language/Loop-Control.md b/docs/Features/Language/Loop-Control.md new file mode 100644 index 0000000..208bbe3 --- /dev/null +++ b/docs/Features/Language/Loop-Control.md @@ -0,0 +1,15 @@ +--- +title: Loop Control +parent: Language Syntax +nav_order: 12 +permalink: /Features/Language/Loop-Control +--- + +# Loop Control + +The following new statements are available for controlling the procession of loops: + +- `Continue For` - Proceed to the next iteration (or end) of `For` loop. +- `Continue While` - Proceed to the next iteration (or end) of `While` loop. +- `Continue Do` - Proceed to the next iteration of `Do` loop. +- `Exit While` - Exit a `While` loop immediately. diff --git a/docs/Features/Language/Module-Organization.md b/docs/Features/Language/Module-Organization.md new file mode 100644 index 0000000..3155875 --- /dev/null +++ b/docs/Features/Language/Module-Organization.md @@ -0,0 +1,35 @@ +--- +title: Module Organization +parent: Language Syntax +nav_order: 16 +permalink: /Features/Language/Module-Organization +--- + +# Module-Level Code Organization + +It's now possible to insert module-level code in between methods or properties. Where previously all `Declare` statements, `Enum`, `Type`, etc had to appear prior to the first `Sub/Function/Property`, the following would now be valid: + +```vb +Private Const foo = "foo" +Sub SomeMethod() +'... +End Sub +Private Const bar = "bar" +Sub SomeOtherMethod() +'... +End Sub +``` + +## Preset Methods for Code Part Names + +The following can be used and what they represent will be automatically inserted as a `String`: + +- `CurrentComponentName`, e.g. "Form1" +- `CurrentProcedureName`, e.g. "Foo" when in `Sub Foo()` +- `CurrentProjectName` +- `CurrentSourceFile` +- `CurrentComponentCLSID` + +## Removal of Limits + +twinBASIC imposes no artificial limitations on line continuations, procedure size, number of controls on a form, module size, and more. diff --git a/docs/Features/Language/Operators.md b/docs/Features/Language/Operators.md new file mode 100644 index 0000000..f2f8930 --- /dev/null +++ b/docs/Features/Language/Operators.md @@ -0,0 +1,36 @@ +--- +title: Operators +parent: Language Syntax +nav_order: 7 +permalink: /Features/Language/Operators +--- + +# New Operators + +twinBASIC introduces several new operators to enhance language capabilities. + +## Bitshift Operators + +`<<` and `>>` perform left-shift and right-shift operations on a numeric variable. Note that shifts beyond available size result in 0, not wrapping. + +## Short-Circuit Conditional Operators + +### OrElse and AndAlso + +With the regular `Or` and `And` statements, both sides are evaluated, even when not necessary. With a short circuit operator, if the condition is resolved by the first side, the other side is not evaluated. So if you have: +`If Condition1 OrElse Condition2 Then`, if Condition1 is `True`, then `Condition2` will not be evaluated, and any code called by it will not run. + +### If() Operator + +Short-circuit `If()` operator with syntax identical to the tradition `IIf`. This has the additional benefit of not converting variables into a `Variant` if they're the same type; i.e. `If(condition, Long, Long)` the `Long` variables will never become a `Variant`. + +## Assignment Operators + +`+= -= /= *= ^= &= <<= >>=` + +These are the equivalent of `var = var (operand) (var2)`. So `i += 1` is the equivalent of `i = i + 1`. + +## IsNot Operator + +The logical opposite of the *Is* operator for testing object equivalence. For example, instead of `If (object Is Nothing) = False` you could now write `If object IsNot Nothing Then`. + diff --git a/docs/Features/Language/Overloading.md b/docs/Features/Language/Overloading.md new file mode 100644 index 0000000..7e1dd14 --- /dev/null +++ b/docs/Features/Language/Overloading.md @@ -0,0 +1,46 @@ +--- +title: Overloading +parent: Language Syntax +nav_order: 6 +permalink: /Features/Language/Overloading +--- + +# Overloading + +twinBASIC supports overloading in two ways: + +## Overloading by Type of Argument + +The following Subs are valid together in a module/class/etc: + +```vb +Sub foo(bar As Integer) +'... +End Sub + +Sub foo(bar As Long) +'... +End Sub + +Sub foo(bar As Double) +'... +End Sub +``` + +The compiler will automatically pick which one is called by the data type. + +## Overloading by Number of Arguments + +In addition to the above, you could also add the following: + +```vb +Sub Foo(bar1 As Integer) +'... +End Sub + +Sub Foo(bar1 As Integer, bar2 As Integer) +'... +End Sub +``` + +The compiler will automatically pick which one is called by the number and/or types of arguments. diff --git a/docs/Features/Language/Pointers.md b/docs/Features/Language/Pointers.md new file mode 100644 index 0000000..1b58fcc --- /dev/null +++ b/docs/Features/Language/Pointers.md @@ -0,0 +1,134 @@ +--- +title: Enhanced Pointer Functionality +parent: Language Syntax +nav_order: 10 +permalink: /Features/Language/Pointers +--- + +# Enhanced Pointer Functionality + +twinBASIC provides several enhancements for working with pointers. + +## ByVal Nothing + +Additionally, while not strictly new syntax, twinBASIC also adds support for `ByVal Nothing`, to override a `ByRef ` argument and pass a null pointer there. + +## vbNullPtr + +Allows passing null pointers to UDT members of APIs/interfaces. The equivalent behavior in VBx is to declare them `As Any` and then pass `ByVal 0` at call sites. + +### Example + +```vb +Type Foo + bar As Long +End Type +Public Declare PtrSafe Function MyFunc Lib "MyDLL" (pFoo As Foo) As Long + +Private Sub CallMyFunc() + Dim ret As Long = MyFunc(vbNullPtr) +End Sub +``` + +## CType(Of \) + +The `CType(Of )` operator specifies an explicit intent to cast one type to another. This can be used for casting `LongPtr` (or `Long` on 32bit/`LongLong` on 64bit) to a custom user-defined type, with or without making a copy of it, depending on the usage. This allows not just for casting directly without a `CopyMemory` call, but also, setting the members of a UDT represented only by a pointer, without copying memory back and forth. + +### Example + +Consider the following UDTs: + +```vb +Private Type foo + a As Long + b As Long + pfizz As LongPtr 'A pointer to a variable of type fizz +End Type +Private Type bar + pfoo As LongPtr 'A pointer to a variable of type foo +End Type +Private Type fizz + c As Long +End Type +``` + +The following code examples work to manipulate the pointers: + +```vb +Sub call1() + Dim f As foo + test1 VarPtr(f) + Debug.Print f.a, f.b +End Sub + +Sub test1(ByVal ptr As LongPtr) + With CType(Of foo)(ptr) + .a = 1 + .b = 2 + End With +End Sub +``` + +This will print `1 2`. + +```vb +Sub call2() + Dim f As foo, b As bar + b.pfoo = VarPtr(f) + test2 b + Debug.Print f.a, f.b +End Sub + +Sub test2(b As bar) + With CType(Of foo)(b.pfoo) + .a = 3 + .b = 4 + End With +End Sub +``` + +This will print `3 4`. + +```vb +Sub call3() + Dim f As foo, b As bar, z As fizz + f.pfizz = VarPtr(z) + b.pfoo = VarPtr(f) + test3 b + Debug.Print z.c +End Sub + +Sub test3(b As bar) + CType(Of fizz)(CType(Of foo)(b.pfoo).pfizz).c = 4 +End Sub +``` + +This will print `4`. Free standing use and nesting is also allowed; the above will print `4`. While the examples here are local code only, this is particularly useful for APIs, where you're forced to work with pointers extensively. + +## Substitute Pointers for UDTs + +In both APIs and local methods, any argument taking a user-defined type can instead be passed a `ByVal LongPtr`, with the new special constant `vbNullPtr` used for a null pointer: + +```vb +Public Declare PtrSafe Function CreateFileW Lib "kernel32" (ByVal lpFileName As LongPtr, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As SECURITY_ATTRIBUTES, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As LongPtr) As LongPtr + +hFile = CreateFileW(StrPtr("name"), 0, 0, ByVal vbNullPtr, '...) +'---or--- +Dim pSec As SECURITY_ATTRIBUTES +Dim lPtr As LongPtr = VarPtr(pSec) +hFile = CreateFileW(StrPtr("name"), 0, 0, ByVal lPtr, '...) +``` + +## Len/LenB(Of \) Support + +The classic `Len` and `LenB` functions can now be used to directly get the length/size of a type, both intrinsic and user-defined, without needing have declared a variable of that type. For instance, to know the pointer size, you can use `LenB(Of LongPtr)`. + +## Improvements to AddressOf + +`AddressOf` can be now be used on class/form/usercontrol members, including from outside the class by specifying the instance. Also, no need for `FARPROC`-type functions, you can use it like `Ptr = AddressOf Func`. So if you have class `CFoo` with member function `bar`, the following is valid: + +```vb +Dim foo1 As New CFoo +Dim lpfn As LongPtr = AddressOf foo1.bar +``` + diff --git a/docs/Features/Language/Return.md b/docs/Features/Language/Return.md new file mode 100644 index 0000000..6826d4a --- /dev/null +++ b/docs/Features/Language/Return.md @@ -0,0 +1,35 @@ +--- +title: Return Syntax +parent: Language Syntax +nav_order: 13 +permalink: /Features/Language/Return +--- + +# Return Syntax + +You can now combine assigning a return value and exiting a procedure into a single statement like many other languages allow. This is accomplished with the `Return` keyword: + +```vb +Private Function Foo() As Long + Dim i As Long = 1 + If i Then + Return i + End If +End Function +``` + +This is the equivalent of: + +```vb +Private Function Foo() As Long + Dim i As Long = 1 + If i Then + Foo = i + Exit Function + End If +End Function +``` + +`Return` can be used to return objects as well. It is currently only valid with a value specified and within a function; you cannot use `Return` without anything after it in a sub. + +`Return` can be used in function procedures, function method procedures, and property getter procedures. diff --git a/docs/Features/Language/Type-Inference.md b/docs/Features/Language/Type-Inference.md new file mode 100644 index 0000000..da09d58 --- /dev/null +++ b/docs/Features/Language/Type-Inference.md @@ -0,0 +1,18 @@ +--- +title: Type Inference +parent: Language Syntax +nav_order: 9 +permalink: /Features/Language/Type-Inference +--- + +# Type Inference + +Variables can now be declared `As Any` and their type will be inferred, similar to C++'s `auto`. + +## Usage + +`Dim x As Any = 5&` would result in x being a `Long`. + +## Limitations + +This is only for the `Dim` statement; arguments cannot be `As Any` except in API declarations. diff --git a/docs/Features/Language/UDTs.md b/docs/Features/Language/UDTs.md new file mode 100644 index 0000000..92b9810 --- /dev/null +++ b/docs/Features/Language/UDTs.md @@ -0,0 +1,92 @@ +--- +title: UDT Enhancements +parent: Language Syntax +nav_order: 11 +permalink: /Features/Language/UDTs +--- + +# Enhancements to User-Defined Types (UDTs) + +## Procedures, Constructors, Destructors, and Operators + +You can now place methods inside UDTs, as well as API declarations. With APIs, if the first parameter is named `Me` and is the same type as the UDT, it's treated as an implicit member call: + +```vb +Type HWND + Value As LongPtr ' the raw HWND + Public DeclareWide PtrSafe Function BringWindowToTop Lib "user32" (ByVal Me As HWND) As Long +End Type +'... +myHwnd.BringWindowToTop() +``` + +There is also a constructor (**Type_Initialize**), and destructor (**Type_Terminate**), assignment operator (**Type_Assignment**), type conversion operator (**Type_Conversion**), and debugger string operator (**Type_DebugView**). These make it possible to create lightweight objects, like a C++ class: + +```vb +Type myType + a As Long + + Private Sub Type_Initialize() + ' NOTE: currently you can only access the UDT members using the "Me." prefix + End Sub + + Private Sub Type_Assignment(ByVal RHS As Variant) ' TIP: You can change the RHS type, and you can define multiple assignment functions + ' NOTE: currently you can only access the UDT members using the "Me." prefix + End Sub + + Private Function Type_Conversion() As Variant ' TIP: you can change the return type here, and you can define multiple conversion functions + ' NOTE: currently you can only access the UDT members using the "Me." prefix + End Function + + Private Function Type_DebugView() As String + ' NOTE: currently you can only access the UDT members using the "Me." prefix + End Function + + Private Sub Type_Terminate() + ' NOTE: currently you can only access the UDT members using the "Me." prefix + End Sub + +End Type +``` + +UDTs of these types are still stack allocated structs that can be used with standard Win32 APIs. + +## Custom UDT Packing + +If you've done extensive work with the Windows API, every so often you'll come across user-defined types that have an extraneous member added called pad, padding, reserved, etc, that doesn't appear in the documentation for that type. This is the result of the UDT applying packing rules different from the default. + +By default, UDTs have hidden spacing bytes that make their largest sized member appear at a multiple of it's size, and making the entire UDT be a multiple of that size. Consider the following UDT: + +```vb +Private Type MyUDT + x As Integer + y As Long + z As Integer +End Type +Private t As MyUDT +``` + +If you ask for `Len(t)`, you get 8-- the sum of 2x2-byte Integers and 1 4-byte Long. But if you ask for `LenB(t)`, you get 12. This is because the largest size type is 4, so that's the packing alignment number. Each Long must appear at a multiple of 4 bytes, so 2 byte of hidden padding is inserted between x and y. You can see this for yourself by checking `VarPtr(t.y) - VarPtr(t)`. This gives you the starting offset of `y`-- which is 4, not 2 like you'd get if it immediately followed `x`. Finally, with the hidden 2 bytes, we're now up to 10 bytes. But the total UDT size must be a multiple of 4, so 2 more hidden bytes are added on the end. + +Some API UDTs will look like `MyUDT` is correct, but you'll see it defined in VBx as 2 Longs-- which gets the required 8 bytes, with some special handling for the first member. If you refer back to the original C/C++ header, you'll find, for this situation, something like `#include ` or `#pragma pack(push,1)` somewhere before the UDT. This manually alters the packing rule to insert no hidden bytes anywhere. + +### [PackingAlignment] Attribute + +twinBASIC normally aligns objects naturally within UDTs, e.g. an 8-byte object is aligned at the 8-byte boundary relative to the beginning of the UDT. This can leave gaps between UDT fields. A tighter packing can be achieved with a smaller **PackingAlignment**: + +```vb +[PackingAlignment(2)] +Private Type MyUDT + x As Integer + y As Long + z As Integer +End Type +Private t As MyUDT +Debug.Assert Len(t) = 8 And LenB(t) = 8 +``` + +You'll now find that both `Len(t)` and `LenB(t)` are 8. + +> [!NOTE] +> +> Alignment, not packing alignment, is not set this way. Specifying 16 would not get you a 16-byte structure for `t`. twinBASIC does not currently have an equivalent for `__declspec_align(n)`, but such a feature is planned. This is rare outside kernel mode programming. diff --git a/docs/Features/Language/index.md b/docs/Features/Language/index.md new file mode 100644 index 0000000..dd4645a --- /dev/null +++ b/docs/Features/Language/index.md @@ -0,0 +1,32 @@ +--- +title: Language Syntax +parent: Features +nav_order: 2 +permalink: /Features/Language/ +has_toc: false +--- + +# Language Syntax + +twinBASIC introduces numerous enhancements to VBx language syntax, including new data types, improved type systems, and modern programming constructs. + +## Topics + +- [Alias Types](Alias-Types) - alias types, similar to **typedef** in C and **using** in C++ +- [Data Types](Data-Types) - New data types (**LongPtr**, **LongLong**, **Decimal**) +- [Interfaces and Coclasses](Interfaces-CoClasses) - Native interface and coclass definitions +- [Inheritance](Inheritance) - **Implements Via** and **Inherits** keywords +- [Delegates](Delegates) - Function pointers, also called delegates +- [Generics](Generics) - Generic type support +- [Overloading](Overloading) - Method overloading capabilities +- [Operators](Operators) - New operators and syntax +- [Literals](Literals) - Binary literals and digit grouping +- [Type Inference](Type-Inference) - **As Any** type inference +- [Pointers](Pointers) - Enhanced pointer functionality +- [UDT Enhancements](UDTs) - User-defined type improvements +- [Loop Control](Loop-Control) - **Continue ...** and **Exit While** +- [Return Syntax](Return) - Modern **Return** statement +- [Inline Initialization](Inline-Initialization) - Variable initialization +- [Handler Methods](Handlers) - **Handles** and **Implements** syntax +- [Module Organization](Module-Organization) - Code placement flexibility +- [Comments](Comments) - New code comment syntax diff --git a/docs/Features/Overview.md b/docs/Features/Overview.md deleted file mode 100644 index 3444422..0000000 --- a/docs/Features/Overview.md +++ /dev/null @@ -1,1471 +0,0 @@ ---- -title: Overview -parent: Features -nav_order: 1 -permalink: /Features/Overview ---- - -# Overview - -This page is intended to list and briefly describe all new features that twinBASIC brings compared to VBx, and assumes existing familiarity with the principles of programming in the BASIC language. They are categorized into the following sections: - -* [Attributes](#attributes) -* [64bit Compilation](#64bit-compilation) -* [Language Syntax](#language-syntax) -* [Project Configuration](#project-configuration) -* [Standard Library Enhancement](#standard-library-enhancements) -* [GUI components](#gui-components) (e.g. controls and forms) -* [Design Experience and Compiler Features](#design-experience-and-compiler-features) - -# Attributes -Attributes have two major functions: - -* they can act as instructions to compiler to influence how code is generated, or - -* to annotate Forms, Modules, Classes, Types, Enums, Declares, and [procedures](../tB/Gloss#procedure) i.e. Subs/Functions/Properties. - -Previously in VBx, these attributes, such as procedure description, hidden, default member, and others, were set via hidden text the IDE's editor didn't show you, configured via the Procedure Attributes dialog or some other places. In tB, these are all visible in the code editor. The legacy ones from VBx are supported for compatibility, but new attributes utilize the following syntax:\ - `[Attribute]` or `[Attribute(value)]` - -Many new attributes enable the powerful additional language features twinBASIC provides, so some of the following items have their associated attributes included in their description. - -See also [the comprehensive reference for attributes](../tB/Core/Attributes). - -# 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`, e.g.:\ -`Public Declare PtrSafe Sub foo Lib "bar" (ByVal hWnd As LongPtr)` - -> [!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). - -# Language Syntax - -## New data types -* `LongPtr` Meant primarily to handle pointers, `LongPtr` is a 4-byte (32 bits) signed integer in 32bit mode, and a signed 8-byte integer (64 bits) in 64bit mode. -* `LongLong` A signed 8-byte (64 bits) integer, ranging from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. Note that this type is available in both 32bit and 64bit mode (VBA restricts it to 64bit mode). -* `Decimal` In twinBASIC, `Decimal` is implementented as a full, regular data type, in addition to use within a `Variant`. This is a 16-byte (128 bits) type which holds a 12-byte (96 bits) integer with variable decimal point scaling and sign bit information. Values range from -79,228,162,514,264,337,593,543,950,335 to 79,228,162,514,264,337,593,543,950,335. -* All of the datatype management features also exist for these types: `DefDec`/`DefLngLng`/`DefLongPtr`, `CDec`/`CLngLng`/`CLongPtr`, and `vbDecimal`/`vbLongLong`/`vbLongPtr` constants for type checking. - -## Interfaces, coclasses, and aliases - -twinBASIC supports these features as native language syntax where in VBx they were only supported via Type Libraries. - -### Defining interfaces -twinBASIC supports defining COM interfaces using BASIC syntax, rather than needing an type library with IDL and C++. These are only supported in .twin files, not in legacy .bas or .cls files. They must appear *before* the `Class` or `Module` statement, and will always have a project-wide scope. the The generic form for this is as follows: - -``` -[InterfaceId ("00000000-0000-0000-0000-000000000000")] -** -Interface Extends - ** - - ** - - ... -End Interface -``` -Methods can be any of the following: `Sub`, `Function`, `Property Get`, `Property Let`, or `Property Set`, with arguments following the standard syntax, and with the standard attributes available. These cannot be modified with `Public/Private/Friend`. `End ` is not used, as these are prototype definitions only. - -Available attributes for interfaces currently include: -* `[Description("text")]` - Provides a description in information popups, and is exported as a `helpstring` attribute in the type library (if applicable). - -* `[Hidden]` - Hides the interface from certain Intellisense and other lists. - -* `[Restricted]` - Restricts the interface methods from being called in most contexts. - -* `[OleAutomation(True/False)]` - Controls whether this attribute is applied in the typelibrary. This attribute is set to **True** by default. - -* `[ComImport]` - Specifies that an interface is an import from an external COM library, for instance, the Windows shell. - -* `[ComExtensible(True/False)]` - Specifies whether new members added at runtime can be called by name through an interface implementing IDispatch. This attribute is set to **False** by default. - -Available attributes for methods currently include: - -* `[Description("text")]` - See above. - -* `[PreserveSig]` - For COM interfaces, normally methods return an HRESULT that the language hides from you. The `[ PreserveSig ]` attribute overrides this behavior and defines the function exactly as you provide. This is neccessary if you need to define it as returning something other than a 4-byte `Long`, or want to handle the result yourself, bypassing the normal runtime error raised if the return value is negative (this is helpful when a negative value indicates an expected, acceptable failure, rather than a true error, like when an enum interface is out of items). - -* `[DispId(number)]` - Defines a dispatch ID associated with the method. - -#### Example - -``` -[InterfaceId("E7064791-0E4A-425B-8C8F-08802AAFEE61")] -[Description("Defines the IFoo interface")] -[OleAutomation(False)] -Interface IFoo Extends IUnknown - Sub MySub(Arg1 As Long) - Function Clone() As IFoo - [PreserveSig] - Function MyFunc([TypeHint(MyEnum)] Arg1 As Variant) As Boolean -End Interface -``` -(Where MyEnum is a standard `Enum ... End Enum` block. - - -### Defining coclasses -In addition to interfaces, twinBASIC also allows defining coclasses -- creatable classes that implement one or more defined interfaces. Like interfaces, these too must be in .twin files and not legacy .bas/.cls files, and must appear prior to the `Class` or `Module` statement. The generic form is: - -``` -[CoClassId("00000000-0000-0000-0000-000000000000")] -** -CoClass - [Default] Interface - *[Default, Source] Interface * - ** -End CoClass -``` - -Each coclass must specify at least one interface but may have several more. It can optionally mark an interface as default or a source. It is typical and highly recommended that an interface be marked with `[Default]` attribute and in cases where it has events to also specify `[Default, Source]` to indicate the default interface used for events. Each represents a contract that the class will provide an implementation of the given interface. Note that at this time, twinBASIC does not yet support defining `dispinterface` interfaces (aka, dispatch-only interface) the usual form of source interfaces for events. - -The attributes available for coclasses are as follows: -* `[Description("text")]` - Provides a description in info popups and other places. -* `[ComCreatable(True/False)]` - Indicates that this coclass can be created with the `New` keyword. This is *True* by default. -* `[AppObject]` - Indicates the class is part of the global namespace. You should not include this attribute without a full understanding of the meaning. -* `[Hidden]` - Hides the coclass from appearing in certain places. -* `[CoClassCustomConstructor("fully qualified path to factory method")]` - Allows custom logic for creating and returning a new instance of the coclass' implementation. - -#### Example - -``` -[CoClassId("52112FA1-FBE4-11CA-B5DD-0020AFE7292D")] -CoClass Foo - [Default] Interface IFoo - Interface IBar -End CoClass -``` -Where `IFoo` and `IBar` are interface defined with the `Interface` syntax described earlier. - -For custom constructor, you should provide a method on a module to create the instance. Here's an example of how this can be achieved. - -``` -[InterfaceId("016BC30A-A8E0-4AAF-93AE-13BD838A149E"))] -Public Interface IFoo - Sub Foo() -End Interface - -[InterfaceId("2A20E655-30A4-4534-86BC-6A7E281C425D")] -Public Interface IBar - Sub Bar() -End Interface - -[CoClassId("7980D953-10BF-478C-93BB-DD0093315D96")] -[CoClassCustomConstructor("FooFactory.CreateFoo")] -[COMCreatable(True)] -Public CoClass Foo - [Default] Interface IFoo - Interface IBar -End CoClass - -' The implementation do not have to be exposed. The coclass is a sufficient description -' and we should implement the interfaces that the coclass exposes. -Private Class FooImpl - Implements IFoo - Implements IBar - - Public Sub Foo() Implements IFoo.Foo - Debug.Print "Foo ran" - End Sub - - Public Sub Bar() Implements IBar.Bar - Debug.Print "Bar ran" - End Sub -End Class - -Public Module FooFactory - ' The signature must be "preserved", returning a HRESULT - ' and the new instance via the "out" parameter. - ' Note that we new up the FooImpl but return the Foo coclass. - Public Function CreateFoo(ByRef RHS As Foo) As Long - Set RHS = New FooImpl - Return 0 ' S_OK - End Function -End Module - -Public Module Test - Public Sub DoIt() - Dim MyFoo As Foo - ' create a new instance of coclass Foo - ' this implicilty calls the custom constructor - ' in the FooFactory. - Set MyFoo = New Foo - MyFoo.Foo - End Sub -End Module -``` - -### Defining Aliases - -An alias is an alternative name for a User-Defined Type, intrinsic type, or interface. This is similar to C/C++'s `typedef` statement. These can then be used in place of the original type and will be treated as if the original was used (would not be a type mismatch). - -`[Public|Private] Alias AltName As OrigName` - -#### Example - -With intrinsic types, or if you have a type such as - -```vb -Public Type POINT - x As Long - y As Long -End Type -``` -You can create aliases: - -```vb -Public Alias POINTAPI As POINT - -Public Alias CBoolean As Byte - -Public Alias KAFFINITY As LongPtr -``` - -Like interfaces and coclasses, these must be placed in a .twin file, outside of `Module` and `Class` blocks. You can create aliases of other aliases. The optional `Public` and `Private` modifiers determine whether the alias is exported to the Type Library of an ActiveX DLL or Control. A `Private` alias would result in usage of it being replaced with the original type. - - -### Enhancements to `Implements` -* `Implements` in twinBASIC is allowed on inherited interfaces-- for instance, if you have `Interface IFoo2 Extends IFoo`, you then use `Implements IFoo2` in a class, where in VBx this would not be allowed. You'll need to provide methods for all inherited interfaces (besides `IDispatch` and `IUnknown`). The class will mark all interfaces as available-- you don't need a separate statement for `IFoo`, it will be passed through `Set` statements (and their underlying `QueryInterface` calls) automatically. - -* If you have an interface multiple others extend from, you can write multiple implementations, or specify one implementation for all. For example: - - ``` vb - IOleWindow_GetWindow() As LongPtr _ - Implements IOleWindow.GetWindow, IShellBrowser.GetWindow, IShellView2.GetWindow - ``` - -* `Implements` allowed on interfaces with 'As Any' parameters: In VBx, you'd get an error if you attempted to use any interface containing a member with an `As Any` argument. With twinBASIC, this is allowed if you substitute `As LongPtr` for `As Any`, for example: - - ``` vb - Interface IFoo Extends IUnknown - Sub Bar(ppv As Any) - End Interface - - Class MyClass - Implements IFoo - - Private Sub IFoo_Bar(ppv As LongPtr) Implements IFoo.Bar - - End Sub - ``` - -### `Implements Via` for basic inheritance and `Inherits` for complete inheritance and full Object-Oriented Programming (OOP) - -#### `Implements Via` - -tB allows simple inheritance among classes. For example, if you have class cVehicle which implements IVehicle containing method Honk, you could create child classes like cCar or cTruck, which inherit the methods of the original, so you could call cCar.Honk without writing a separate implementation. Here's what this looks like as code: - -![image](Images/b0724fe2-636d-47db-a8fc-531a585ddaf9.png) - -You can see that the Honk method is only implemented by the parent class, then called from the child class when you click the CodeLens button to run the sub in place from the IDE. - -#### `Inherits` - -This is a more robust option for full inheritance and OOP. It supports `Protected` methods and variables that are accessible to derived classes but not outside callers, `Overridable` and `Overrides` syntax, multiple inheritance, and explicit base class constructors. Sample 23 demonstrates this: - -Starting with a base class, - -``` vb -Private Class Animal - Protected _name As String - Protected _dob As Date ' date of birth - - Public Event Spoke(ByVal sound As String) - - Public Sub New(name As String, dob As Date) - _name = name - _dob = dob - End Sub - - Public Property Get Name() As String - Name = _name - End Property - - Public Property Get DOB() As Date - DOB = _dob - End Property - - ' Age in whole years based on DOB and today's date - Public Function AgeYears() As Long - Dim y As Long - y = DateDiff("yyyy", _dob, Date) - If DateSerial(Year(Date), Month(_dob), Day(_dob)) > Date Then y = y - 1 - AgeYears = y - End Function - - Public Sub Speak() - Dim s As String - s = GetSound() - RaiseEvent Spoke(s) - Debug.Print _name & " says: " & s - End Sub - - ' --- Overridable hook for derived classes --- - Protected Overridable Function GetSound() As String - GetSound = "" - End Function -End Class -``` - -others can inherit: - -``` vb -' ===== Derived: Dog ===== -Private Class Dog - Inherits Animal - - Protected _breed As String - - Public Sub New(name As String, dob As Date, breed As String) - Animal.New(name, dob) ' we can explicitly call base constructors from within our constructor - _breed = breed - End Sub - - Public Property Get Breed() As String - Breed = _breed - End Property - - ' Override: - Protected Overridable Function GetSound() As String Overrides Animal.GetSound - GetSound = "woof" - End Function -End Class - -' ===== Further derived: GuardDog (Dog → GuardDog) ===== -Private Class GuardDog - Inherits Dog - - Protected _onDuty As Boolean - - Public Sub New(name As String, dob As Date, breed As String) - Dog.New(name, dob, breed) ' we can explicitly call base constructors from within our constructor - _onDuty = True - End Sub - - Public Property Get OnDuty() As Boolean - OnDuty = _onDuty - End Property - Public Property Let OnDuty(ByVal v As Boolean) - _onDuty = v - End Property - - ' Multi-level override (overriding Dog's override): - Protected Function GetSound() As String Overrides Dog.GetSound - If _onDuty Then - GetSound = "WOOF!" - Else - GetSound = "woof" - End If - End Function -End Class -``` - -This is just an excerpt, see the full Sample 23 for additional classes, usage, and information about inheritance in twinBASIC. - - -## Delegate types for Call By Pointer - -There is native support for calling a function by pointer, by way of `Delegate` syntax. A delegate in twinBASIC is a function pointer type that's compatible with LongPtr. `AddressOf` returns a delegate type, that's also backwards compatible with `LongPtr`. - -The syntax looks like this: - -``` vb - Private Delegate Function Delegate1 (ByVal A As Long, ByVal B As Long) As Long - - Private Sub Command1_Click() - Dim myDelegate As Delegate1 = AddressOf Addition - MsgBox "Answer: " & myDelegate(5, 6) - End Sub - - Public Function Addition(ByVal A As Long, ByVal B As Long) As Long - Return A + B - End Function -``` - -The delegate type can also be used in interface/API declarations and as members of a User-defined type, for example, the `ChooseColor` API: - -``` vb -Public Delegate Function CCHookProc (ByVal hwnd As LongPtr, ByVal uMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr -Public Type CHOOSECOLOR - lStructSize As Long - hwndOwner As LongPtr - hInstance As LongPtr - rgbResult As Long - lpCustColors As LongPtr - Flags As ChooseColorFlags - lCustData As LongPtr - lpfnHook As CCHookProc 'Delegate function pointer type instead of LongPtr - lpTemplateName As LongPtr -End Type -``` - -If you already have code assigning a `Long`/`LongPtr` to the `lpfnHook` member, it will continue to work normally, but now you can also have the type safety benefits of setting it to a method matching the Delegate: - -``` vb -Dim tCC As CHOOSECOLOR -tCC.lpfnHook = AddressOf ChooseColorHookProc - -'... - -Public Function ChooseColorHookProc(ByVal hwnd As LongPtr, ByVal uMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr - -End Function -``` - - -## 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 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 - -Generically: - - Import Libary "Relative resource path" As NAMESPACE Link "dependency1", "dependency2", '... -``` - -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. - -## `Emit()` and Naked functions to insert assembly directly into exe/dll - -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. - -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.) - -## Type Inference - -Variables can now be declared `As Any` and their type will be inferred, similar to C++'s `auto`.\ -`Dim x As Any = 5&` would result in x being a `Long`. - -This is only for the `Dim` statement; arguments cannot be `As Any` except in API declarations. - -## New operators -* Bitshift operators ``<<`` and ``>>`` perform left-shift and right-shift operations on a numeric variable. Note that shifts beyond available size result in 0, not wrapping. - -* `vbNullPtr` - Allows passing null pointers to UDT members of APIs/interfaces. The equivalent behavior in VBx is to declare them `As Any` and then pass `ByVal 0` at call sites. - - **Example** - ``` vb - Type Foo - bar As Long - End Type - Public Declare PtrSafe Function MyFunc Lib "MyDLL" (pFoo As Foo) As Long - - Private Sub CallMyFunc() - Dim ret As Long = MyFunc(vbNullPtr) - End Sub - ``` - -Additionally, while not strictly new syntax, twinBASIC also adds support for `ByVal Nothing`, to override a `ByRef ` argument and pass a null pointer there. - -* Short-circuit conditional operators `OrElse` and `AndAlso`. With the regular `Or` and `And` statements, both sides are evaluated, even when not necessary. With a short circuit operator, if the condition is resolved by the first side, the other side is not evaluated. So if you have: -`If Condition1 OrElse Condition2 Then`, if Condition1 is `True`, then `Condition2` will not be evaluated, and any code called by it will not run. - -* Short-circuit `If()` operator with syntax identical to the tradition `IIf`. This has the additional benefit of not converting variables into a `Variant` if they're the same type; i.e. `If(condition, Long, Long)` the `Long` variables will never become a `Variant`. - -* New assignment operators: `+= -= /= *= ^= &= <<= >>=` - - These are the equivalent of `var = var (operand) (var2)`. So `i += 1` is the equivalent of `i = i + 1`. - -* `IsNot` operator: The logical opposite of the *Is* operator for testing object equivalence. For example, instead of `If (object Is Nothing) = False` you could now write `If object IsNot Nothing Then`. - -## New literals notation - -### Binary literals -In addition to `&H` for hexadecimal literals and `&O` for octal notation, twinBASIC also provides `&B` for binary notation. For example, `Dim b As Long = &B010110` is valid syntax, and b = 22. - -### Digit grouping -The `&H`, `&O`, and `&B` literals can all be grouped using an underscore, for example, grouping a `Long` by it's constituent binary byte groups: `&B10110101_10100011_10000011_01101110`, or grouping a `LongLong` as two `Long` groups: `&H01234567_89ABCDEF`. - -## 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. - - -## Improvements to `AddressOf` -`AddressOf` can be now be used on class/form/usercontrol members, including from outside the class by specifying the instance. Also, no need for `FARPROC`-type functions, you can use it like `Ptr = AddressOf Func`. So if you have class `CFoo` with member function `bar`, the following is valid: - -``` vb -Dim foo1 As New CFoo -Dim lpfn As LongPtr = AddressOf foo1.bar -``` - -## Enhanced pointer functionality - -### `CType(Of )` -The `CType(Of )` operator specifies an explicit intent to cast one type to another. This can be used for casting `LongPtr` (or `Long` on 32bit/`LongLong` on 64bit) to a custom user-defined type, with or without making a copy of it, depending on the usage. This allows not just for casting directly without a `CopyMemory` call, but also, setting the members of a UDT represented only by a pointer, without copying memory back and forth. - -Consider the following UDTs: - -``` vb -Private Type foo - a As Long - b As Long - pfizz As LongPtr 'A pointer to a variable of type fizz -End Type -Private Type bar - pfoo As LongPtr 'A pointer to a variable of type foo -End Type -Private Type fizz - c As Long -End Type -``` - -The following codes examples work to manipulate the pointers: - -``` vb -Sub call1() - Dim f As foo - test1 VarPtr(f) - Debug.Print f.a, f.b -End Sub -Sub test1(ByVal ptr As LongPtr) - With CType(Of foo)(ptr) - .a = 1 - .b = 2 - End With -End Sub -``` - -This will print `1 2`. - -``` vb -Sub call2() - Dim f As foo, b As bar - b.pfoo = VarPtr(f) - test2 b - Debug.Print f.a, f.b -End Sub -Sub test2(b As bar) - With CType(Of foo)(b.pfoo) - .a = 3 - .b = 4 - End With -End Sub -``` -This will print `3 4` - -``` vb -Sub call3() - Dim f As foo, b As bar, z As fizz - f.pfizz = VarPtr(z) - b.pfoo = VarPtr(f) - test3 b - Debug.Print z.c -End Sub -Sub test3(b As bar) - CType(Of fizz)(CType(Of foo)(b.pfoo).pfizz).c = 4 -End Sub -``` - -Free standing use and nesting is also allowed; the above will print `4`. While the examples here are local code only, this is particularly useful for APIs, where you're forced to work with pointers extensively. - -### Substitute pointers for UDTs - -In both APIs and local methods, any argument taking a user-defined type can instead be passed a `ByVal LongPtr`, with the new special constant `vbNullPtr` used for a null pointer: - -``` vb -Public Declare PtrSafe Function CreateFileW Lib "kernel32" (ByVal lpFileName As LongPtr, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As SECURITY_ATTRIBUTES, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As LongPtr) As LongPtr - -hFile = CreateFileW(StrPtr("name"), 0, 0, ByVal vbNullPtr, '...) -'---or--- -Dim pSec As SECURITY_ATTRIBUTES -Dim lPtr As LongPtr = VarPtr(pSec) -hFile = CreateFileW(StrPtr("name"), 0, 0, ByVal lPtr, '...) -``` - - -## `Len/LenB(Of )` Support -The classic `Len` and `LenB` functions can now be used to directly get the length/size of a type, both intrinsic and user-defined, without needing have declared a variable of that type. For instance, to know the pointer size, you can use `LenB(Of LongPtr)`. - -## Overloading - -twinBASIC supports overloading in two ways: - -### Overloading by type of argument -The following Subs are valid together in a module/class/etc: - -``` vb -Sub foo(bar As Integer) -'... -End Sub - -Sub foo(bar As Long) -'... -End Sub - -Sub foo(bar As Double) -'... -End Sub -``` -The compiler will automatically pick which one is called by the data type. - -### Overloading by number of arguments -In addition to the above, you could also add the following: - -``` vb -Sub Foo(bar1 As Integer) -'... -End Sub -Sub Foo(bar1 As Integer, bar2 As Integer) -'... -End Sub -``` -The compiler will automatically pick which one is called by the number and/or types of arguments. - -## Inline variable initialization -You can now set initial values for variables inline, without needing a line-continuation character. - -**Examples** - -``` vb -Dim i As Long = 1 -Dim foo As Boolean = bar() -Dim arr As Variant = Array(1, 2, 3) -Dim strArr(2) As String = Array("a", "b", "c") -Dim cMC As cMyClass = New cMyClass(customConstructorArgs) -``` - -## Inline variable declaration for `For` -You now no longer need a separate `Dim` statement for counter variables: - -``` vb -For i As Long = 0 To 10 - '... -Next -``` -is now valid syntax. You can use any type, not just `Long`. - -## Generics -Generics have basic support in methods and classes. - -``` vb -Public Function TCast(Of T)(ByRef Expression As T) As T - Return Expression -End Function -``` -Which could be used e.g. to return a `Date` typed variable with `TCast(Of Date)("2021-01-01")` - -A Class generic allows the type in methods throughout the class. The following example shows this to make a generic List class: - -``` -[COMCreatable(False)] -Class List(Of T) - Private src() As T - Private c As Long - Sub New(p() As T) - src = p - End Sub - [DefaultMember] - Function GetAt(ByVal idx As Long) As T - Return src(idx) - End Function - Public Property Get Count() As Long - Return c - End Property -End Class -``` - -This could then be used as follows: - -``` vb -Private Sub TestListGenericClass() - Dim names As List(Of String) = New List(Of String)(Array("John", "Smith", "Kane", "Tessa", "Yoland", "Royce", "Samuel")) - Dim s As String = names(0) - Debug.Print s -End Sub -``` - -## Enhancements to API/method declarations -### `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 -``` - -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. - -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 -``` - -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 -``` -[PreserveSig(False)] -Public Declare PtrSafe Function SHGetDesktopFolder Lib "shell32" () As IShellFolder` -``` - - -## Loop control -The following new statements are available for controlling the procession of loops: - -* `Continue For` - Proceed to the next iteration (or end) of `For` loop. -* `Continue While` - Proceed to the next iteration (or end) of `While` loop. -* `Continue Do` - Proceed to the next iteration of `Do` loop. -* `Exit While` - Exit a `While` loop immediately. - -## `Return` syntax for functions. -You can now combine assigning a return value and exiting a function into a single statement like many other languages allow. This is accomplished with the `Return` keyword: - -``` vb -Private Function Foo() As Long -Dim i As Long = 1 -If i Then - Return i -End If -End Function -``` -this is the equivalent of - -``` vb -Private Function Foo() As Long -Dim i As Long = 1 -If i Then - Foo = i - Exit Function -End If -End Function -``` -`Return` can be used for objects as well. It is currently only valid with a value specified and within a function; you cannot use `Return` without anything after it in a sub. - -## New class member handler syntax -You can now separate the name of method from the class member it applies to. - -### `Handles` for events -For events events on Forms, UserControls, and event-raising objects, you can define any method as the handler, rather than need to name it as `Object_Event()`, by following it with `Handles Object.Event`. For example, in a form, instead of `Private Sub Form_Load()` you could handle the `Load` event with `Private Sub OnLoad() Handles Form.Load`. - -### `Implements` for interfaces -Similar to the above, for forms/UCs/classes that use `Implements`, you can use `Sub Bar() Implements IFoo.Bar`. Note that you can specify more than one implemented method; for more information, see the Enhancements to Implements subsection of the Interfaces and Coclasses section detailing the new in-language syntax for defining these. - -> [!NOTE] -> These are opt-in and optional\ -> For compatibility, twinBASIC will always continue to support the traditional syntax for event handling and Implements, and you're not required to use this new syntax (or *any* of the additions described in this article). Whether or not automatically created prototypes use this syntax is controlled via IDE Options: "IDE: Use new handles/implements syntax". - -## Enhancements to user-defined types (UDTs) - -### Procedures and events - -You can now place methods inside UDTs, as well as API declarations. With APIs, if the first parameter is named `Me` and is the same type as the UDT, it's treated as an implicit member call, - -``` vb -Type HWND - Value As LongPtr ' the raw HWND - Public DeclareWide PtrSafe Function BringWindowToTop Lib "user32" (ByVal Me As HWND) As Long -End Type -'... -myHwnd.BringWindowToTop() -``` - -There's also associated events, including a constructor and destructor that make it possible to create lightweight objects, like a C++ class; -``` vb -Type myType - a As Long - - Private Sub Type_Initialize() - ' NOTE: currently you can only access the UDT members using the "Me." prefix - End Sub - - Private Sub Type_Assignment(ByVal RHS As Variant) ' TIP: You can change the RHS type, and you can define multiple assignment functions - ' NOTE: currently you can only access the UDT members using the "Me." prefix - End Sub - - Private Function Type_Conversion() As Variant ' TIP: you can change the return type here, and you can define multiple conversion functions - ' NOTE: currently you can only access the UDT members using the "Me." prefix - End Function - - Private Function Type_DebugView() As String - ' NOTE: currently you can only access the UDT members using the "Me." prefix - End Function - - Private Sub Type_Terminate() - ' NOTE: currently you can only access the UDT members using the "Me." prefix - End Sub - -End Type -``` - -UDTs of these types are still stack allocated structs that can be used with standard Win32 APIs. - - -### Custom UDT packing -If you've done extensive work with the Windows API, every so often you'll come across user-defined types that have an extraneous member added called pad, padding, reserved, etc, that doesn't appear in the documentation for that type. This is the result of the UDT applying packing rules different from the default. By default, UDTs have hidden spacing bytes that make their largest sized member appear at a multiple of it's size, and making the entire UDT be a multiple of that size. Consider the following UDT: -``` vb -Private Type MyUDT - x As Integer - y As Long - z As Integer -End Type -Private t As MyUDT -``` -If you ask for `Len(t)`, you get 8-- the sum of 2x2-byte Integers and 1 4-byte Long. But if you ask for `LenB(t)`, you get 12. This is because the largest size type is 4, so that's the packing alignment number. Each Long must appear at a multiple of 4 bytes, so 2 byte of hidden padding is inserted between x and y. You can see this for yourself by checking `VarPtr(t.y) - VarPtr(t)`. This gives you the starting offset of `y`-- which is 4, not 2 like you'd get if it immediately followed `x`. Finally, with the hidden 2 bytes, we're now up to 10 bytes. But the total UDT size must be a multiple of 4, so 2 more hidden bytes are added on the end.\ -Some API UDTs will look like `MyUDT` is correct, but you'll see it defined in VBx as 2 Longs-- which gets the required 8 bytes, with some special handling for the first member. If you refer back to the original C/C++ header, you'll find, for this situation, something like `#include ` or `#pragma pack(push,1)` somewhere before the UDT. This manually alters the packing rule to insert no hidden bytes anywhere.\ -In twinBASIC, instead of two Longs and having to worry about getting the first one right when it's not an Integer, you can use the original definition with: -``` -[PackingAlignment(1)] -Private Type MyUDT - x As Integer - y As Long - z As Integer -End Type -Private t As MyUDT -``` -You'll now find that both `Len(t)` and `LenB(t)` are 8. **NOTE:** Alignment, not packing alignment, is not set this way-- specifying 16 would not get you a 16-byte structure for `t`. twinBASIC does not currently have an equivalent for `__declspec_align(n)`, but such a feature is planned. This is very, very rare outside kernel mode programming. - -## Block and inline comments - -You can now use `/* */` syntax. For example, `Sub Foo(bar As Long /* out */)` or: - -```c -/* -Everything here is -a comment until: -*/ -``` - - -## Destructuring assignment support for arrays -This feature allows you to assign the contents of an array to multiple variables in a single line: - -``` vb - Dim a As Long, b As Long, c As Long - Dim d(2) As Long - d(0) = 1 - d(1) = 2 - d(2) = 3 - Array(a, b, c) = d - Debug.Print a, b, c -``` - -This would print `1 2 3`. You could also assign multiple variables at once like this and get the same result: - -``` vb - Dim a As Long, b As Long, c As Long - Array(a, b, c) = Array(1, 2, 3) - Debug.Print a, b, c -``` - -You can now also do assignments like this: - -``` vb - Dim a As Long = 9 - Dim b As Long = 7 - Dim c() As Long = Array(a, b) - Debug.Print c(1), UBound(c) -``` - -Which prints `7 1`. - - -## Direct access to COM error handling -You can retrieve the last `HRESULT` to a COM interface call via `Err.LastHResult`; these are usually hidden and mapped to internal errors-- everything in a COM interface normally called a `Sub` is actually an `HRESULT`-returning function. - -More importantly, you can now **set** the `HRESULT` in interface implementations with `Err.ReturnHResult`. This was a critical missing feature for which sometimes Err.Raise would work, but mostly programmers resorted to complicated vtable-swapping code to redirect to a standard module function. For instance you can now return `S_FALSE` where expected with `Err.ReturnHResult = S_FALSE`. - -## Module-level definitions not limited to top -It's now possible to insert module-level code in between methods or properties. Where previously all `Declare` statements, `Enum`, `Type`, etc had to appear prior to the first `Sub/Function/Property`, the following would now be valid: - -``` vb -Private Const foo = "foo" -Sub SomeMethod() -'... -End Sub -Private Const bar = "bar" -Sub SomeOtherMethod() -'... -End Sub -``` - -## Preset methods for code part names -The following can be used and what they represent will be automatically inserted as a `String`: - -* `CurrentComponentName`, e.g. "Form1" -* `CurrentProcedureName`, e.g. "Foo" when in `Sub Foo()` -* `CurrentProjectName`, -* `CurrentSourceFile` -* `CurrentComponentCLSID` - - -## Removal of limits on line continuations, procedure size, etc. -twinBASIC imposes no artificial limitations on those, number of controls on a form, module size, and more. - -## 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. 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 it's 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, e.g. - -``` -[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 primary used to create Standard DLLs (see next section), but this functionality is also available in Standard EXE and other compiled project types. - -# Project Configuration -## Built in support for making Standard DLLs -While it was possible to accomplish this via hacks previously, tB offers it as a built in project type. You can choose this project type at startup, then you simply need to mark functions with `[DllExport]` when you want them exported. The name will be used as-is, it will not be mangled. The `CDecl` calling convention is supported with the normal syntax, e.g. `Public Function foo CDecl(bar As Long) As Long`.\ -Standard DLLs in twinBASIC can still specify a startup point; each export will then check if this code has run yet, and if not, run it. - -## Built in support for making Console Applications -This project type allows making a true console project rather than a GUI project. Helpfully, it will also add a default `Console` class for reading/writing console IO and provided debug console. - -## Built in support for easily making services. -tB has a services package (WinServicesLib) that makes creating full featured true services a breeze. It simplifies use of MESSAGETABLE resources, multiple services per exe, named pipes for IPC, and more. See samples 21-22. - -## Built in support for making Kernel-Mode Drivers. -Kernel mode drivers can only access a very limited subset of the API, and can't call usermode DLLs like a runtime. So it would typically require elaborate hacks and dramatically limit what you could do in prior BASIC products, if possible at all. And of course, there's no WOW64 layer for kernel mode, so tB is the first BASIC product to support making drivers for 64bit Windows. This is controlled by the 'Project: Native subsystem' option, as well as the following two features. - -## Built in support for overriding entry point. -BASIC applications typically have a hidden entry point that is the first to run, before `Sub Main` or the startup form's `Form_Load`. This sets up features of the app like initializing COM. twinBASIC supports overriding this and setting one of your own procedures as the true entry point. This is mostly useful for kernel mode projects, which must have a specific kind of entry point and can't call the normal APIs in the default. But there are other reasons you might want to use this option, but be warned: Many things will break in a normal application if you don't do the initialization procedures yourself or understand precisely what you can't use. - -## Place API declares in the IAT -tB has the option to put all API declares in the import address table rather than call them at runtime via `LoadLibrary/GetProcAddress` like VBx (which puts TLB-declared APIs in the import table; tB replicates this too but further provides an option for in-project declares). - -This has a small performance advantage in that it's loaded and bound at startup rather than on the first call, but the primary use is for kernel mode, which cannot call `LoadLibrary` and other user mode APIs to use late binding. - - -## Register ActiveX builds to `HKEY_LOCAL_MACHINE` or `HKEY_CURRENT_USER` option. -While modern applications use `HKEY_CURRENT_USER`, for VBx compatibility components must be registered to `HKEY_LOCAL_MACHINE`. Note that this requires running as admin when registering. - -## Registration at build time is optional -tB provides the Project: Register DLL after build option so you can disable automatic registration, if for example you wanted to move the file first. - - - -# Standard Library Enhancements - -## Unicode support -Native functions that take string arguments, such as `MsgBox` and FileSystem functions (e.g. `Open`, `Dir`, `Mkdir`, `Kill`, and `RmDir`) now support Unicode. Additionally, .twin files make this easy to use as the editor supports Unicode as well. So you can paste a Unicode string in the editor, see it appear correctly, then have the same string correctly displayed by tB functions and controls. - -### Encoding options for file i/o - -The `Open` statement supports Unicode through the use of a new `Encoding` keyword and variable, and allows you to specify a wide range of encoding options in addition to standard Unicode options. - -Usage example: - -``` vb -Open "C:\MyFile.txt" For Input Encoding utf-8 As #1 -``` - -The full list of encoding options currently defined (and don't worry, these will come up in Intellisense) is: `default_system_ansi`, `utf_7`, `utf_7_bom`, `utf_8`, `utf_8_bom`, `utf_16`, `utf_16_bom`, `us_ascii`, `koi8_u`, `koi8_r`, `big5`, `iso_8859_1_latin1`, `iso_8859_2_latin2`, `iso_8859_3_latin3`, `iso_8859_4_latin4`, `iso_8859_5_cyrillic`, `iso_8859_6_arabic`, `iso_8859_7_greek`, `iso_8859_8_hebrew`, `iso_8859_9_latin5_turkish`, `iso_8859_10_latin6_nordic`, `iso_8859_11_thai`, `iso_8859_13_latin8_baltic`, `iso_8859_14_latin8_celtic`, `iso_8859_15_latin9_euro`, `iso_8859_16_latin10_balkan`, `windows_1250_central_europe`, `windows_1251_cyrillic`, `windows_1252_western`, `windows_1253_greek`, `windows_1254_turkish`, `windows_1255_hebrew`, `windows_1256_arabic`, `windows_1257_baltic`, `windows_1258_vietnamese`, `ibm_850_western_europe`, `ibm_852_central_and_eastern_europe`, `ibm_855_cyrillic`, `ibm_856_hebrew`, `ibm_857_turkish`, `ibm_858_western_europe`, `ibm_860_portuguese`, `ibm_861_icelandic`, `ibm_862_hebrew`, `ibm_863_canadian`, `ibm_865_danish`, `ibm_866_cyrillic`, `ibm_869_greek`, `ibm_932_japanese`, and `ibm_949_korean`. - -Others with a similar format should be accepted depending on system support. - -## New Built-in functions: - -In addition to the new datatype-related and component name functions already described, the standard builtin `VBA` library now includes: - -* `IsArrayInitialized(variable)` - Determines if an array is initialized. Note: A `Variant` declared as empty array with `Array()` will return `True`. -* `RGBA(r, g, b, a)` - Like the `RBG()` function, only including the alpha channel. -* `RBG_R(rgba)`, `RGB_B(rgba)`, `RBG_G(rgba)`, and `RGBA_A(rgba)` - Get the values for individual channels. -* `TranslateColor(ColorValue, Optional Palette)` - Translates an OLE color value to an RGB color. -* `ProcessorArchitecture()` - Returns either `vbArchWin32` or `vbArchWin64`, depending on application bitness. -* `CallByDispId(Object, DispId, CallType, Arguments)` - Similar to `CallByName()`, but uses the dispatch id instead of method name. -* `RaiseEventByName(Object, Name, Args)` - Invokes an event on class, using arguments specified as a single `Variant` containing an array. -* `RaiseEventByName2(Object, Name, Arg1, Arg2, ...)` - Invokes an event on class, using arguments specified as a ParamArray. -* `PictureToByteArray(StdPicture)` - Converts a picture to a byte array; Global.LoadPicture supports loading from byte arrays. -* `CreateGUID()` - Returns a string with a freshly generated GUID. -* `AllocMem(size)` and `FreeMem` - allocate and free memory from the process heap. -* `Int3Breakpoint` - Inserts a true breakpoint helpful for attached external debuggers. -* `GetDeclaredTypeProgId(Of T)` / `GetDeclaredTypeClsid(Of T)` generics for getting strings of ProgID/CLSID. -* `GetDeclaredMinEnumValue(Of T)` / `GetDeclaredMaxEnumValue(Of T)` generics -* Some `Interlocked*` functions - -### Built in runtime functions and redirects from msvbvm60.dll - -tB has built in support for some of the most commonly used runtime functions, for compatibility. These all support both 32 and 64bit. Unless otherwise noted, all of these function in two ways: First, built in native versions that are always present (unless you remove the basic compiler packages), with the most common arrangements of arguments. These don't require a `Declare` statement. If you *do* provide a `Declare` version, tB will allow whatever arrangements of arguments you specify (e.g. `As Any` instead of `As LongPtr`), mapped to an alias if provided. - -* Memory functions: `GetMem1`, `GetMem2`, `GetMem4`, `GetMem8`, `PutMem1`, `PutMem2`, `PutMem4`, `PutMem8` with new additions `GetMemPtr` and `PutMemPtr` pegged to the current pointer size. -* `vbaObjSet`, `vbaObjSetAddref`, `vbaCastObj`, and `vbaObjAddref` for manipulating object assignments through pointers. -* `vbaCopyBytes` and `vbaCopyBytesZero` -* `vbaAryMove` and `vbaRefVarAry` (currently only with a `Declare` statement). -* tB also has an instrinsic `VarPtr` but will still redirect calls via a declare statement, e.g. aliases used for arrays (though tB's `VarPtr` supports arrays natively). - -### New `App` object properties - -* `App.IsInIDE` - `True` when running from the IDE. -* `App.IsElevated` - Returns whether the program is currently running with administrator rights. -* `App.LastBuildPath` - Returns the full path of the last build. Does not persist between compiler/IDE restarts. -* `App.Build` - For the additional version number field. -* `App.ModulePath` - Returns the full path of the currently executing module. For example, if placed in a DLL and called from an EXE, the path of the DLL would be returned. Also, the twinBASIC debugger DLL is given when running from the IDE, when the method is in the app itself. - -# GUI components - -## Support for modern image formats -You no longer face an incredibly limited format selection for images in tB Forms and Controls; not only do the Bitmap and Icon formats support the full range of formats for those, you can additionally load PNG Images, JPEG Images, Metafiles (.emf/.wmf), and SVG Vector Graphics (.svg). - -### Improved `LoadPicture` -Additionally, `LoadPicture` can load all image types directly from a byte array, rather than requiring a file on disk. You can use this to load images from resource files or other sources. Note that if your projects references stdole2.tlb (most do), currently you must qualify it as `Global.LoadPicture` to get tB's custom binding that supports byte arrays. - -### High quality scaling in Image controls -Image controls now offer a `StretchMode` property that allows you to choose Bilinear, Bicubic, Lanczos3 and Lanczos8 strectching algorithms, which are far superior to the default stretching algorithm. These use built in algorithms so do not add additional depencies or API calls. - -## Transparency and Alpha Blending on Forms - -## Form.TransparencyKey -This new property specifies a color that will be transparent to the window below it in the z-order (all windows, not just in your project). Setting this property will cause the specified color to be 100% transparent. A Shape control with a solid `FillStyle` is a helpful tool to color the areas of the form in the key color. - -## Form.Opacity - -This sets an alpha blending level for the entire form. Like transparency, this is to all windows immediately underneath it. Note that any areas covered by the `TransparencyKey` color will remain 100% transparent. - -The following image shows a Form with a `TransparencyKey` of Red, using a Shape control to define the transparent area, while also specifying 75% `Opacity` for the entire form: - -![image alt ><](Images/85f25aa2-abc8-4d42-8510-078f8ee4a324.png) - -## Additional Form features - -In addition to the above, forms have: - -* `DpiScaleX`/`DpiScaleY` properties to retrieve the current values -* `.MinWidth`, `.MinHeight`, `.MaxWidth`, and `.MaxHeight` properties so subclassing isn't needed for this -* `Form.TopMost` property. -* Control anchoring: control x/y/cx/cy can made relative, so they're automatically moved/resized with the Form. For example if you put a TextBox in the bottom right, then check the Right and Bottom anchors (in addition to Top and Left), the bottom right will size with the form on resize. This saves a lot of boiler-plate sizing code. -* Control docking: Controls can be fixed along one of the sides of the Form (or container), or made to fill the whole Form/container. Multiple controls can be combined and mixed/matched in docking positions. - -For more information on Control Anchoring and Control Docking, see the Wiki entry [Control Anchoring and Docking ‐ Automatic size and position management](Anchoring-Docking). - -## Unicode support -All tB-implemented controls support Unicode, both in the code editor and when displayed. - -**Important:** If you subclass controls, note that this means you will receive the Unicode (W) version of window messages, e.g. ListViews will send `LVN_GETDISPINFOW (LVN_FIRST - 77)` instead of `LVN_GETDISPINFOA (LVN_FIRST - 50)`. - -## UserControl Enhancements - -The UserControl object now provides the new Boolean property `PreKeyEvents` that enables corresponding new events `PreKeyDown` and `PreKeyUp`. These allow handling special keys like tab, arrows, etc without OS or COM hooks (for example, based on the `IOleInPlaceActiveObject` interface). These work with all child windows inside the UserControl, including ones created by `CreateWindowEx`. You can also access raw message data in the `PreKeyDown`/`PreKeyUp` event handlers with the new `PreKeyWParam`/`PreKeyLParam` and `PreKeyTargetHwnd` UserControl properties. - - -## Control Modernization - -tB will eventually replace all built in controls that you're used to, for now the ones available are: CommandButton, TextBox, ComboBox, CheckBox, OptionButton, Label, Frame, PictureBox, Line, Shape, VScrollBar, HScrollBar, Timer, DriveListBox, DirListBox, FileListBox, Image, and Data from the basic set; then, ListView, TreeView, ProgressBar, DTPicker, MonthView, Slider, and UpDown from the Common Controls. - -* Controls support x64: Every control can be compiled both as 32bit and 64bit without changing anything.\ -* Controls are DPI aware: They will automatically size correctly when dpi awareness is enabled for your app.\ -* Controls support Visual Styles per-control: Comctl6 styles can be applied, or not, on a control-by-control basis with the `.VisualStyles` property. - -### Alternatives for unimplemented controls - -The best option is Krool's VBCCR and VBFlexGrid projects. These are now available [from the Package Server](../Packages/Importing-TWINSERV/) in x64-compatible form, and are also DPI aware and support Visual Styles. - -Additionally, the original OCX controls provided by Microsoft will work fine; however, they're mostly 32-bit only. The x64 version of `MSComCtl.ocx` doesn't come with Windows and isn't legally redistributable but if you have Office 64bit, it works in tB. - -## Misc additional control properties and enhancements - -* `TextBox.NumbersOnly` property: Restricts input to 0-9 by setting the `ES_NUMBER` style on the underlying control. - -* `TextBox.TextHint` property: Sets the light gray hint text in an empty TextBox (`EM_SETCUEBANNER`). - -* `PictureDpiScaling` property for forms, usercontrols and pictureboxes: PictureDpiScaling property allows you to turn off DPI scaling of images so that they display at 1:1 rather than allowing the OS to stretch them. The idea being you may want to choose a different bitmap manually, rather than apply the somewhat limited OS-stretching. - -* `Label.VerticalAlignment` property: Defaults to Top. - -* `Label.LineSpacing` property (in twips, default is 0) - -* `Label.Angle` property (in degrees, rotates the label text) - -* `Label.BorderCustom` property (has suboptions to set size, padding and color of borders independently for each side). - -* `Timer.Interval` can now be set to any positive `Long` instead of being limited to 65,535. - -* `StrConv()` now has `vbUTF8` / `vbFromUTF8` - -## New Controls - -### QR Code Control -![image](Images/54ed49d8-b434-45e3-9e63-a1fe75cdf814.png) - -Easily display custom QR codes with a native control. - -### Multiframe Control -![image-15](Images/4ad9c774-b31d-47d3-9963-6d99ac4f37bb.png) - -This control allows you to create a number of frames within it with their size specified as a percentage, such that as the control is resized the frames within expand proportionally. For details and a video demonstration, Mike Wolfe's twinBASIC Weekly Update [covered it when released](https://nolongerset.com/twinbasic-update-april-29-2025/#experimental-multi-frame-control).\ -Combined with anchors and docking, this allows designing highly functional and complex layouts visually, without writing any code to handling resizing. - -### CheckMark Control -![image](Images/5fc60b7b-4f54-445c-8504-451019b7ec55.png) - -Primarily intended for reports but available in Forms and UserControls as well, the CheckMark control provides a scalable check component where this is fixed to a single size in a normal CheckBox control. - -# Design Experience and Compiler Features - -## Customize COM initialization -You can specify the call used by the hidden entry point with the following options: `CoInitialize STA`, `CoInitializeEx MTA`, `OleInitialize STA`. If you don't know the difference, don't change it from the default. - -## Customize symbol table parameters - -You can adjust the following parameters: Max Size Raw, Max Size Lookup, and Data Type Lookup. These options allow for compiling very large projects that would otherwise have issues, and the compiler will notify you if these values need to be increased. - -## Sanitize Boolean types -Under the hood, a Boolean is a 2-byte type. With memory APIs, or when receiving these from outside code, it's possible to store values other than the ones representing `True` and `False`. This option validates Booleans from external sources, e.g. COM objects and APIs, to ensure only the two supported values are stored. - -## Stale/dangling pointer detection - -Bugs result from using Strings and Variants after they have been freed. It may not be noticed immediately if the memory has not been overwritten, but it's sometimes hard to detect and can cause issues like a String displaying it's previous value or garbage. This debugging option detects use-after-free, and replaces the data with a special symbol indicating the problem. Below shows an example where the ListView ColumnHeader text had been set by previously-freed string and detected by this feature: - -![image](Images/021f6cbf-acce-445d-ade7-3fcad0af4927.png) - -Previously, it had shown the same text for every column-- but only under certain circumstances, leading to the issue being overlooking for a long time. - -## Additional compiler options - -- Projects can be marked `LARGEADDRESSAWARE`. - -- A manual base address can be specified - -- Option to strip PE relocation symbols - -### Exploit mitigation - -You can enable the following: - -Data execution prevention (DEP) - -Address-space layout randomization (ASLR) - -## Debug Trace Logger -New to the debugging experience is a powerful trace logging feature that can automatically create detailed logs to either the debug console or a file. Messages can be output to the new system with `Debug.TracePrint`. The logger works both when running from the IDE and in compiled executables. - -![image](Images/4fc2bf99-2bec-4943-837d-21038d791574.png) - -## Compiler Warnings - -twinBASIC provides compiler warnings during design time for common bad practices or likely oversights, including: -* Warnings for likely-incorrect hex literals\ -Non-explicit values are coerced into the lowest possible type first. So if you declare a constant as `&H8000`, the compiler sees it as an -32,768 `Integer`, and when you're putting that into a `Long` you almost certainly do not want -32,768, you want **positive** 32,768, which requires you to instead use `&H8000&`.\ -This warning is supplied for `&H8000`-`&HFFFF` and `&H80000000`-`&HFFFFFFFF`. - -* Warnings for implicit variable creation with `ReDim`.\ -When you use `ReDim myArray(1)`, the `myArray` variable is created for you, when it's good practice to declare all variables first. - -* Warnings for use of `DefType`\ -This feature is discouraged for it making code difficult to read and prone to difficult to debug errors. - -The full list can be found in your project's Settings page:\ -![image](Images/017bd6f8-4b35-43a9-b6be-84cba69daf64.png) - -### Adjusting warnings -Each warning has the ability to set them to ignore or turn them into an error both project-wide via the Settings page, and per-module/class, and per-procedure with `[IgnoreWarnings(TB___)]`, `[EnforceWarnings(TB____)]`, and `[EnforceErrors(TB____)]` attributes, where the underscores are replaced with the **full** number, e.g. `[IgnoreWarnings(TB0001)]`; the leading zeroes must be included. - -### Strict mode - -twinBASIC has added the following warning messages to support something similar to .NET's Strict Mode, where certain implicit conversions are not allowed and must be made explicit. By default, these are all set to be ignored, and must be enabled in the "Compiler Warnings" section of Project Settings or per-module/procedure with `[EnforceWarnings()]`. All of these can be configured individually and ignored for procedure/module scope with `[IgnoreWarnings()]` - -**TB0018: Impicit narrowing conversion**\ -Such as converting a Long to Integer; if you have `Dim i As Integer, l As Long` then `i = l` will trigger the warning, and `i = CInt(l)` would be required to avoid it. - -**TB0019: Implicit enumeration conversion**\ -When assigning a member of one Enum to a variabled typed as another, such as `Dim day As VbDayOfWeek: day = vbBlack`. The `CType(Of )` operator whose use in pointers was described in the previous section is also used to specify an explicit type conversion in this case; the warning would not be triggered by `day = CType(Of VbDayOfWeek)(vbBlack)`. - -**TB0020: Suspicious interface conversion**\ -If a declared coclass doesn't explicitly name an interface as supported, converting to it will trigger this warning, e.g. - -``` vb -Dim myPic As StdPicture -Dim myFont As StdFont -Set myFont = myPic -``` - -You'd use `Set myFont = CType(OfStdFont)(myPic)` to avoid this warning. - -**TB0021: Implicit enumeration conversion to/from numeric** -Triggered by assigning a numeric literal to a variabled typed as an Enum, such as `Dim day As VbDayOfWeek: day = 1`. To avoid it you'd use `day = CType(Of VbDayOfWeek)(1)`. - -## Run some Subs from the IDE - -The CodeLens feature allows running Subs and Functions, with no arguments and in modules (but not classes/Forms/UserControls) right from the editor without starting the full program. It has robust access to your code; it can access constants, call other functions both instrinsic and user-define, call APIs, and print to the Debug Console.\ -Methods eligible to run with CodeLens (when enabled), have a bar above them that you can click to run:\ -![image](Images/351d0147-cad3-4e16-89e5-0a9e43496740.png) - - -## Modern IDE features - -While the twinBASIC IDE still has a lot of work planned, it already includes a number of features that make life much easier found in other modern IDE, but not the ancient VBx IDEs, including: - -* Fully theme-able, with Dark (default), Light, and Classic (Light) built in, and an easy inheritance-based system to add your own themes via CSS files. - -* Code folding, with foldable custom-defined regions via `#Region "name" ... #End Region` blocks. - -* Fully customizable keyboard shortcuts covering all commands, with ability to save and switch between different sets. - -* Sticky-scroll, which keeps context lines at the top showing major sections of code like module, region, method, `With` blocks, etc. - -* Indent guides, lines drawn along common indent points to help line things up right. - -* Auto-indent on paste. - -* Paste as comment. - -* Full Unicode support in .twin files, so you can use the full Unicode range of the font in your comments and strings. - -* Inline code hints, which provide annotations at the end of blocks for what the block is (see picture). - -* Code mini-map, shows a graphics overview of the code structure alongside the scroll bar, helping to guide your scrolling. - -* Advanced Information popup, which shows offsets for UDT members, their total size via both `Len()` plus `LenB()`, and their alignment; and v-table entry offsets for interfaces and classes, as well as their inheritance chain. - -* A type library viewer for controls and TLB files that displays the full contents in twinBASIC-style syntax rather than ODL. - -* Color-matching for parentheses and brackets. - -* A History panel containing a list of recently modified methods. - -* An Outline panel with selectable categories. - -* Problems panel, provides a list of all current errors and warnings (you can filter to show only one or the other). - -* On the Form Designer, control with `Visible = False` are faded to visually indicate this. Also, pressing and holding Control shows the tab index of each tab stop. - -![image](Images/014a1d28-30af-4a4d-8b9b-83ab6084f00a.png)\ -[Full size](Images/fafaloneIDEscreenshot1.png) - -* New code structure based Project Explorer:\ -![image](Images/9a5c50d5-a9f8-44a7-96f7-ae84548bd7ef.png) - -The classic file-based view is still used by default, you can activate the new view with a toggle button:\ -![image](Images/b000d3aa-3689-4d94-88e3-bca44f8b7de6.png) - - -## Package Server - -Code can be grouped as a package, and published to an online server. You can have Private packages, visible only to you, or Public packages, visible to everyone. - -![image](Images/5951dab6-738e-4b63-83c4-3331ec6d36b9.png) - -For more information, see the following pages: - -[What is a package](../Packages/What-Is) - -[Creating a TWINPACK package](../Packages/Creating-TWINPACK) - -[Importing a package from a TWINPACK file](../Packages/Importing-TWINPACK) - -[Importing a package from TWINSERV](../Packages/Importing-TWINSERV) - -[Updating a package](../Packages/Updating) - -## View Forms and Packages as JSON -Project forms and packages are stored as JSON format data, and you can view this by right-click in Project Explorer and selecting 'View as JSON'. This is particularly interesting for packages as it exposes the entire code in a much more easily parsed format. - -![image](Images/22660f54-ff5d-4b21-93d3-39715f1f35ed.png) - -![image](Images/a6525b1d-ac22-4303-ae27-7984c20eba0c.png) - - ---- - -# Many more to come! - -This list has covered all new features at the present time. There's many more planned, including built-in multithreading syntax, unsigned variable types, cross-platform compilation, and more! If there's a feature you'd like to see, please feel welcome to make a feature request by [posting an issue in the main twinBASIC GitHub repository](https://github.com/twinbasic/twinbasic/issues). diff --git a/docs/Packages/Creating a TWINPACK package.md b/docs/Features/Packages/Creating a TWINPACK package.md similarity index 97% rename from docs/Packages/Creating a TWINPACK package.md rename to docs/Features/Packages/Creating a TWINPACK package.md index 419f613..606590d 100644 --- a/docs/Packages/Creating a TWINPACK package.md +++ b/docs/Features/Packages/Creating a TWINPACK package.md @@ -1,8 +1,10 @@ --- title: Creating a TWINPACK Package parent: Packages -nav_order: 2 -permalink: /Packages/Creating-TWINPACK +nav_order: 1 +permalink: /Features/Packages/Creating-TWINPACK +redirect_from: + - /Packages/Creating-TWINPACK --- # Creating a TWINPACK package diff --git a/docs/Packages/Images/0fa1272d-41d6-4d0f-b19c-f47f24a47c4d.png b/docs/Features/Packages/Images/0fa1272d-41d6-4d0f-b19c-f47f24a47c4d.png similarity index 100% rename from docs/Packages/Images/0fa1272d-41d6-4d0f-b19c-f47f24a47c4d.png rename to docs/Features/Packages/Images/0fa1272d-41d6-4d0f-b19c-f47f24a47c4d.png diff --git a/docs/Packages/Images/4d90f313-35d5-426d-8fc3-852ca03382fa.png b/docs/Features/Packages/Images/4d90f313-35d5-426d-8fc3-852ca03382fa.png similarity index 100% rename from docs/Packages/Images/4d90f313-35d5-426d-8fc3-852ca03382fa.png rename to docs/Features/Packages/Images/4d90f313-35d5-426d-8fc3-852ca03382fa.png diff --git a/docs/Packages/Images/4e4b8e4d-2a1c-42e5-8f4b-5a9b3f523ee8.png b/docs/Features/Packages/Images/4e4b8e4d-2a1c-42e5-8f4b-5a9b3f523ee8.png similarity index 100% rename from docs/Packages/Images/4e4b8e4d-2a1c-42e5-8f4b-5a9b3f523ee8.png rename to docs/Features/Packages/Images/4e4b8e4d-2a1c-42e5-8f4b-5a9b3f523ee8.png diff --git a/docs/Packages/Images/6ad7a172-0e1b-4276-ac89-042681552507.png b/docs/Features/Packages/Images/6ad7a172-0e1b-4276-ac89-042681552507.png similarity index 100% rename from docs/Packages/Images/6ad7a172-0e1b-4276-ac89-042681552507.png rename to docs/Features/Packages/Images/6ad7a172-0e1b-4276-ac89-042681552507.png diff --git a/docs/Packages/Images/8d74d820-9907-4e76-ac42-71d0233187f1.png b/docs/Features/Packages/Images/8d74d820-9907-4e76-ac42-71d0233187f1.png similarity index 100% rename from docs/Packages/Images/8d74d820-9907-4e76-ac42-71d0233187f1.png rename to docs/Features/Packages/Images/8d74d820-9907-4e76-ac42-71d0233187f1.png diff --git a/docs/Packages/Images/9eeffbcf-d73e-4a92-bce5-811ed60aba98.png b/docs/Features/Packages/Images/9eeffbcf-d73e-4a92-bce5-811ed60aba98.png similarity index 100% rename from docs/Packages/Images/9eeffbcf-d73e-4a92-bce5-811ed60aba98.png rename to docs/Features/Packages/Images/9eeffbcf-d73e-4a92-bce5-811ed60aba98.png diff --git a/docs/Packages/Images/a1331a0e-3ba3-45cf-8dc3-2e24f0fa1fe6.png b/docs/Features/Packages/Images/a1331a0e-3ba3-45cf-8dc3-2e24f0fa1fe6.png similarity index 100% rename from docs/Packages/Images/a1331a0e-3ba3-45cf-8dc3-2e24f0fa1fe6.png rename to docs/Features/Packages/Images/a1331a0e-3ba3-45cf-8dc3-2e24f0fa1fe6.png diff --git a/docs/Packages/Images/d9f1e4d9-1805-47e5-93aa-251151b4e914.png b/docs/Features/Packages/Images/d9f1e4d9-1805-47e5-93aa-251151b4e914.png similarity index 100% rename from docs/Packages/Images/d9f1e4d9-1805-47e5-93aa-251151b4e914.png rename to docs/Features/Packages/Images/d9f1e4d9-1805-47e5-93aa-251151b4e914.png diff --git a/docs/Packages/Images/db4636f6-d988-4e31-94a2-c4c170418e81.png b/docs/Features/Packages/Images/db4636f6-d988-4e31-94a2-c4c170418e81.png similarity index 100% rename from docs/Packages/Images/db4636f6-d988-4e31-94a2-c4c170418e81.png rename to docs/Features/Packages/Images/db4636f6-d988-4e31-94a2-c4c170418e81.png diff --git a/docs/Packages/Images/e2a65dfe-4a9d-4524-b6d6-7a6d1bc35cdb.png b/docs/Features/Packages/Images/e2a65dfe-4a9d-4524-b6d6-7a6d1bc35cdb.png similarity index 100% rename from docs/Packages/Images/e2a65dfe-4a9d-4524-b6d6-7a6d1bc35cdb.png rename to docs/Features/Packages/Images/e2a65dfe-4a9d-4524-b6d6-7a6d1bc35cdb.png diff --git a/docs/Packages/Images/e35d5955-9e70-4d6e-abd7-748558da75ba.png b/docs/Features/Packages/Images/e35d5955-9e70-4d6e-abd7-748558da75ba.png similarity index 100% rename from docs/Packages/Images/e35d5955-9e70-4d6e-abd7-748558da75ba.png rename to docs/Features/Packages/Images/e35d5955-9e70-4d6e-abd7-748558da75ba.png diff --git a/docs/Packages/Images/e749e10f-e361-4f15-a977-d756fcb3b5dd.png b/docs/Features/Packages/Images/e749e10f-e361-4f15-a977-d756fcb3b5dd.png similarity index 100% rename from docs/Packages/Images/e749e10f-e361-4f15-a977-d756fcb3b5dd.png rename to docs/Features/Packages/Images/e749e10f-e361-4f15-a977-d756fcb3b5dd.png diff --git a/docs/Packages/Images/e9a3fd21-8e6a-4485-b52c-0c041600826b.png b/docs/Features/Packages/Images/e9a3fd21-8e6a-4485-b52c-0c041600826b.png similarity index 100% rename from docs/Packages/Images/e9a3fd21-8e6a-4485-b52c-0c041600826b.png rename to docs/Features/Packages/Images/e9a3fd21-8e6a-4485-b52c-0c041600826b.png diff --git a/docs/Packages/Images/f2fd8374-fe46-40b0-8c66-2443df4dc5b3.png b/docs/Features/Packages/Images/f2fd8374-fe46-40b0-8c66-2443df4dc5b3.png similarity index 100% rename from docs/Packages/Images/f2fd8374-fe46-40b0-8c66-2443df4dc5b3.png rename to docs/Features/Packages/Images/f2fd8374-fe46-40b0-8c66-2443df4dc5b3.png diff --git a/docs/Packages/Images/packLicenceFiles.png b/docs/Features/Packages/Images/packLicenceFiles.png similarity index 100% rename from docs/Packages/Images/packLicenceFiles.png rename to docs/Features/Packages/Images/packLicenceFiles.png diff --git a/docs/Packages/Images/packPublishButton.png b/docs/Features/Packages/Images/packPublishButton.png similarity index 100% rename from docs/Packages/Images/packPublishButton.png rename to docs/Features/Packages/Images/packPublishButton.png diff --git a/docs/Packages/Images/packPublishComplete1.png b/docs/Features/Packages/Images/packPublishComplete1.png similarity index 100% rename from docs/Packages/Images/packPublishComplete1.png rename to docs/Features/Packages/Images/packPublishComplete1.png diff --git a/docs/Packages/Images/packPublishPackage1.png b/docs/Features/Packages/Images/packPublishPackage1.png similarity index 100% rename from docs/Packages/Images/packPublishPackage1.png rename to docs/Features/Packages/Images/packPublishPackage1.png diff --git a/docs/Packages/Importing a package from TWINSERV.md b/docs/Features/Packages/Importing a package from TWINSERV.md similarity index 92% rename from docs/Packages/Importing a package from TWINSERV.md rename to docs/Features/Packages/Importing a package from TWINSERV.md index deefda5..54cd841 100644 --- a/docs/Packages/Importing a package from TWINSERV.md +++ b/docs/Features/Packages/Importing a package from TWINSERV.md @@ -1,8 +1,10 @@ --- title: Importing a Package from TWINSERV parent: Packages -nav_order: 3 -permalink: /Packages/Importing-TWINSERV +nav_order: 2 +permalink: /Features/Packages/Importing-TWINSERV +redirect_from: + - /Packages/Importing-TWINSERV --- # Importing a package from TWINSERV diff --git a/docs/Packages/Importing a package from a TWINPACK file.md b/docs/Features/Packages/Importing a package from a TWINPACK file.md similarity index 90% rename from docs/Packages/Importing a package from a TWINPACK file.md rename to docs/Features/Packages/Importing a package from a TWINPACK file.md index 5cf16df..a63e680 100644 --- a/docs/Packages/Importing a package from a TWINPACK file.md +++ b/docs/Features/Packages/Importing a package from a TWINPACK file.md @@ -1,8 +1,10 @@ --- title: Importing a Package from a TWINPACK File parent: Packages -nav_order: 4 -permalink: /Packages/Importing-TWINPACK +nav_order: 3 +permalink: /Features/Packages/Importing-TWINPACK +redirect_from: + - /Packages/Importing-TWINPACK --- # Importing a package from a TWINPACK file diff --git a/docs/Packages/Updating a package.md b/docs/Features/Packages/Updating a package.md similarity index 94% rename from docs/Packages/Updating a package.md rename to docs/Features/Packages/Updating a package.md index 544fa1a..0a8b7ad 100644 --- a/docs/Packages/Updating a package.md +++ b/docs/Features/Packages/Updating a package.md @@ -1,8 +1,10 @@ --- title: Updating a Package parent: Packages -nav_order: 5 -permalink: /Packages/Updating +nav_order: 4 +permalink: /Features/Packages/Updating +redirect_from: + - /Packages/Updating --- # Updating a package diff --git a/docs/Packages/What is a package.md b/docs/Features/Packages/index.md similarity index 88% rename from docs/Packages/What is a package.md rename to docs/Features/Packages/index.md index 1330825..73c4ce2 100644 --- a/docs/Packages/What is a package.md +++ b/docs/Features/Packages/index.md @@ -1,11 +1,14 @@ --- -title: What is a Package -parent: Packages -nav_order: 1 -permalink: /Packages/What-Is +title: Packages +parent: Features +nav_order: 6 +permalink: /Features/Packages/ +redirect_from: + - /Packages + - /Packages/What-Is --- -# What is a package? +# Packages In twinBASIC, a *package* is a collection of components that you can reference from another twinBASIC project. The components can be modules, classes or interfaces. @@ -15,4 +18,4 @@ With TWINPACK packages you group common components together into their own names twinBASIC comes complete with a package manager service called TWINSERV, allowing you to easily share and distribute TWINPACK packages to other twinBASIC developers. -Please be aware that TWINPACK files currently contain the full sourcecode of your packaged components. It is planned that we will in future allow for creating binary (compiled) TWINPACK files for developers that hold an Ultimate edition licence of twinBASIC. \ No newline at end of file +Please be aware that TWINPACK files currently contain the full sourcecode of your packaged components. It is planned that we will in future allow for creating binary (compiled) TWINPACK files for developers that hold an Ultimate edition licence of twinBASIC. diff --git a/docs/Features/Project-Configuration/ActiveX-Registration.md b/docs/Features/Project-Configuration/ActiveX-Registration.md new file mode 100644 index 0000000..58090a9 --- /dev/null +++ b/docs/Features/Project-Configuration/ActiveX-Registration.md @@ -0,0 +1,16 @@ +--- +title: ActiveX Registration +parent: Project Configuration +nav_order: 3 +permalink: /Features/Project-Configuration/ActiveX-Registration +--- + +# ActiveX Registration Options + +## Registration Location + +Register ActiveX builds to `HKEY_LOCAL_MACHINE` or `HKEY_CURRENT_USER` option. While modern applications use `HKEY_CURRENT_USER`, for VBx compatibility components must be registered to `HKEY_LOCAL_MACHINE`. Note that this requires running as admin when registering. + +## Build-Time Registration + +Registration at build time is optional. tB provides the Project: Register DLL after build option so you can disable automatic registration, if for example you wanted to move the file first. diff --git a/docs/Features/Project-Configuration/Compiler-Options.md b/docs/Features/Project-Configuration/Compiler-Options.md new file mode 100644 index 0000000..6121448 --- /dev/null +++ b/docs/Features/Project-Configuration/Compiler-Options.md @@ -0,0 +1,35 @@ +--- +title: Compiler Options +parent: Project Configuration +nav_order: 2 +permalink: /Features/Project-Configuration/Compiler-Options +--- + +# Compiler Options + +twinBASIC provides several compiler options to control how your code is compiled and optimized. + +## COM Initialization + +You can specify the call used by the hidden entry point with the following options: `CoInitialize STA`, `CoInitializeEx MTA`, `OleInitialize STA`. If you don't know the difference, don't change it from the default. + +## Symbol Table Parameters + +You can adjust the following parameters: Max Size Raw, Max Size Lookup, and Data Type Lookup. These options allow for compiling very large projects that would otherwise have issues, and the compiler will notify you if these values need to be increased. + +## Boolean Type Sanitization + +Under the hood, a Boolean is a 2-byte type. With memory APIs, or when receiving these from outside code, it's possible to store values other than the ones representing `True` and `False`. This option validates Booleans from external sources, e.g. COM objects and APIs, to ensure only the two supported values are stored. + +## Additional Options + +- **LARGEADDRESSAWARE**: Projects can be marked `LARGEADDRESSAWARE`. +- **Base Address**: A manual base address can be specified. +- **PE Relocation Symbols**: Option to strip PE relocation symbols. + +## Exploit Mitigation + +You can enable the following security features: + +- **Data execution prevention (DEP)** +- **Address-space layout randomization (ASLR)** diff --git a/docs/Features/Project-Configuration/Project-Types.md b/docs/Features/Project-Configuration/Project-Types.md new file mode 100644 index 0000000..7e98773 --- /dev/null +++ b/docs/Features/Project-Configuration/Project-Types.md @@ -0,0 +1,38 @@ +--- +title: Project Types +parent: Project Configuration +nav_order: 1 +permalink: /Features/Project-Configuration/Project-Types +--- + +# Project Types + +twinBASIC provides built-in support for several project types beyond the traditional EXE and ActiveX DLL/Control. + +## Standard DLLs + +While it was possible to accomplish this via hacks previously, tB offers it as a built in project type. You can choose this project type at startup, then you simply need to mark functions with `[DllExport]` when you want them exported. The name will be used as-is, it will not be mangled. The `CDecl` calling convention is supported with the normal syntax, e.g. `Public Function foo CDecl(bar As Long) As Long`. + +Standard DLLs in twinBASIC can still specify a startup point; each export will then check if this code has run yet, and if not, run it. + +## Console Applications + +This project type allows making a true console project rather than a GUI project. Helpfully, it will also add a default `Console` class for reading/writing console IO and provided debug console. + +## Windows Services + +tB has a services package (WinServicesLib) that makes creating full featured true services a breeze. It simplifies use of MESSAGETABLE resources, multiple services per exe, named pipes for IPC, and more. See samples 21-22. + +## Kernel-Mode Drivers + +Kernel mode drivers can only access a very limited subset of the API, and can't call usermode DLLs like a runtime. So it would typically require elaborate hacks and dramatically limit what you could do in prior BASIC products, if possible at all. And of course, there's no WOW64 layer for kernel mode, so tB is the first BASIC product to support making drivers for 64bit Windows. This is controlled by the 'Project: Native subsystem' option, as well as the following two features: + +### Overriding Entry Point + +BASIC applications typically have a hidden entry point that is the first to run, before `Sub Main` or the startup form's `Form_Load`. This sets up features of the app like initializing COM. twinBASIC supports overriding this and setting one of your own procedures as the true entry point. This is mostly useful for kernel mode projects, which must have a specific kind of entry point and can't call the normal APIs in the default. But there are other reasons you might want to use this option, but be warned: Many things will break in a normal application if you don't do the initialization procedures yourself or understand precisely what you can't use. + +### Place API Declares in the IAT + +tB has the option to put all API declares in the import address table rather than call them at runtime via `LoadLibrary/GetProcAddress` like VBx (which puts TLB-declared APIs in the import table; tB replicates this too but further provides an option for in-project declares). + +This has a small performance advantage in that it's loaded and bound at startup rather than on the first call, but the primary use is for kernel mode, which cannot call `LoadLibrary` and other user mode APIs to use late binding. diff --git a/docs/Features/Project-Configuration/index.md b/docs/Features/Project-Configuration/index.md new file mode 100644 index 0000000..426eea5 --- /dev/null +++ b/docs/Features/Project-Configuration/index.md @@ -0,0 +1,17 @@ +--- +title: Project Configuration +parent: Features +nav_order: 3 +permalink: /Features/Project-Configuration/ +has_toc: false +--- + +# Project Configuration + +twinBASIC offers various project configuration options and new project types to suit different development needs. + +## Topics + +- [Project Types](Project-Types) - Standard DLL, Console, Services, and Kernel Drivers +- [Compiler Options](Compiler-Options) - Entry point, IAT placement, and more +- [ActiveX Registration Options](ActiveX-Registration) - Registration settings for ActiveX projects diff --git a/docs/Features/Standard-Library/File-IO.md b/docs/Features/Standard-Library/File-IO.md new file mode 100644 index 0000000..8d82b1e --- /dev/null +++ b/docs/Features/Standard-Library/File-IO.md @@ -0,0 +1,42 @@ +--- +title: File I/O +parent: Standard Library +nav_order: 2 +--- + +# Encoding Options for File I/O + +The `Open` statement supports Unicode through the use of a new `Encoding` keyword and variable, and allows you to specify a wide range of encoding options in addition to standard Unicode options. + +## Usage Example + +```vb +Open "C:\MyFile.txt" For Input Encoding utf-8 As #1 +``` + +## Supported Encodings + +The full list of encoding options currently defined (and don't worry, these will come up in Intellisense) is: + +- `default_system_ansi` +- `utf_7`, `utf_7_bom` +- `utf_8`, `utf_8_bom` +- `utf_16`, `utf_16_bom` +- `us_ascii` +- `koi8_u`, `koi8_r` +- `big5` +- `iso_8859_1_latin1`, `iso_8859_2_latin2`, `iso_8859_3_latin3`, `iso_8859_4_latin4` +- `iso_8859_5_cyrillic`, `iso_8859_6_arabic`, `iso_8859_7_greek`, `iso_8859_8_hebrew` +- `iso_8859_9_latin5_turkish`, `iso_8859_10_latin6_nordic`, `iso_8859_11_thai` +- `iso_8859_13_latin8_baltic`, `iso_8859_14_latin8_celtic` +- `iso_8859_15_latin9_euro`, `iso_8859_16_latin10_balkan` +- `windows_1250_central_europe`, `windows_1251_cyrillic`, `windows_1252_western` +- `windows_1253_greek`, `windows_1254_turkish`, `windows_1255_hebrew`, `windows_1256_arabic` +- `windows_1257_baltic`, `windows_1258_vietnamese` +- `ibm_850_western_europe`, `ibm_852_central_and_eastern_europe` +- `ibm_855_cyrillic`, `ibm_856_hebrew`, `ibm_857_turkish`, `ibm_858_western_europe` +- `ibm_860_portuguese`, `ibm_861_icelandic`, `ibm_862_hebrew` +- `ibm_863_canadian`, `ibm_865_danish`, `ibm_866_cyrillic`, `ibm_869_greek` +- `ibm_932_japanese`, and `ibm_949_korean` + +Others with a similar format should be accepted depending on system support. diff --git a/docs/Features/Standard-Library/New-Functions.md b/docs/Features/Standard-Library/New-Functions.md new file mode 100644 index 0000000..651b225 --- /dev/null +++ b/docs/Features/Standard-Library/New-Functions.md @@ -0,0 +1,98 @@ +--- +title: New Functions +parent: Standard Library +nav_order: 3 +permalink: /Features/Standard-Library/New-Functions +--- + +# New Built-in Functions + +In addition to the new datatype-related and component name functions already described, the standard builtin `VBA` library now includes many new capabilities. + +## New Functions + +- `IsArrayInitialized(variable)` - Determines if an array is initialized. Note: A `Variant` declared as empty array with `Array()` will return `True`. +- `RGBA(r, g, b, a)` - Like the `RBG()` function, only including the alpha channel. +- `RBG_R(rgba)`, `RGB_B(rgba)`, `RBG_G(rgba)`, and `RGBA_A(rgba)` - Get the values for individual channels. +- `TranslateColor(ColorValue, Optional Palette)` - Translates an OLE color value to an RGB color. +- `ProcessorArchitecture()` - Returns either `vbArchWin32` or `vbArchWin64`, depending on application bitness. +- `CallByDispId(Object, DispId, CallType, Arguments)` - Similar to `CallByName()`, but uses the dispatch id instead of method name. +- `RaiseEventByName(Object, Name, Args)` - Invokes an event on class, using arguments specified as a single `Variant` containing an array. +- `RaiseEventByName2(Object, Name, Arg1, Arg2, ...)` - Invokes an event on class, using arguments specified as a ParamArray. +- `PictureToByteArray(StdPicture)` - Converts a picture to a byte array; Global.LoadPicture supports loading from byte arrays. +- `CreateGUID()` - Returns a string with a freshly generated GUID. +- `AllocMem(size)` and `FreeMem` - allocate and free memory from the process heap. +- `Int3Breakpoint` - Inserts a true breakpoint helpful for attached external debuggers. +- `GetDeclaredTypeProgId(Of T)` / `GetDeclaredTypeClsid(Of T)` generics for getting strings of ProgID/CLSID. +- `GetDeclaredMinEnumValue(Of T)` / `GetDeclaredMaxEnumValue(Of T)` generics. +- Some `Interlocked*` functions + +## Runtime Functions from msvbvm60.dll + +tB has built in support for some of the most commonly used runtime functions, for compatibility. These all support both 32 and 64bit. Unless otherwise noted, all of these function in two ways: First, built in native versions that are always present (unless you remove the basic compiler packages), with the most common arrangements of arguments. These don't require a `Declare` statement. If you *do* provide a `Declare` version, tB will allow whatever arrangements of arguments you specify (e.g. `As Any` instead of `As LongPtr`), mapped to an alias if provided. + +### Memory Functions + +- `GetMem1`, `GetMem2`, `GetMem4`, `GetMem8`, `PutMem1`, `PutMem2`, `PutMem4`, `PutMem8` +- New additions `GetMemPtr` and `PutMemPtr` pegged to the current pointer size + +### Object Manipulation + +- `vbaObjSet`, `vbaObjSetAddref`, `vbaCastObj`, and `vbaObjAddref` for manipulating object assignments through pointers. + +### Array Operations + +- `vbaCopyBytes` and `vbaCopyBytesZero` +- `vbaAryMove` and `vbaRefVarAry` (currently only with a `Declare` statement). +- tB also has an instrinsic `VarPtr` but will still redirect calls via a declare statement, e.g. aliases used for arrays (though tB's `VarPtr` supports arrays natively). + +## New App Object Properties + +- `App.IsInIDE` - `True` when running from the IDE. +- `App.IsElevated` - Returns whether the program is currently running with administrator rights. +- `App.LastBuildPath` - Returns the full path of the last build. Does not persist between compiler/IDE restarts. +- `App.Build` - For the additional version number field. +- `App.ModulePath` - Returns the full path of the currently executing module. For example, if placed in a DLL and called from an EXE, the path of the DLL would be returned. Also, the twinBASIC debugger DLL is given when running from the IDE, when the method is in the app itself. + +## COM Error Handling + +### Direct Access to COM Errors + +You can retrieve the last `HRESULT` to a COM interface call via `Err.LastHResult`; these are usually hidden and mapped to internal errors-- everything in a COM interface normally called a `Sub` is actually an `HRESULT`-returning function. + +### Setting Return HRESULT + +More importantly, you can now **set** the `HRESULT` in interface implementations with `Err.ReturnHResult`. This was a critical missing feature for which sometimes Err.Raise would work, but mostly programmers resorted to complicated vtable-swapping code to redirect to a standard module function. For instance you can now return `S_FALSE` where expected with `Err.ReturnHResult = S_FALSE`. + +## Destructuring Assignment + +This feature allows you to assign the contents of an array to multiple variables in a single line: + +```vb +Dim a As Long, b As Long, c As Long +Dim d(2) As Long +d(0) = 1 +d(1) = 2 +d(2) = 3 +Array(a, b, c) = d +Debug.Print a, b, c +``` + +This would print `1 2 3`. You could also assign multiple variables at once like this and get the same result: + +```vb +Dim a As Long, b As Long, c As Long +Array(a, b, c) = Array(1, 2, 3) +Debug.Print a, b, c +``` + +You can now also do assignments like this: + +```vb +Dim a As Long = 9 +Dim b As Long = 7 +Dim c() As Long = Array(a, b) +Debug.Print c(1), UBound(c) +``` + +Which prints `7 1`. diff --git a/docs/Features/Standard-Library/Unicode-Support.md b/docs/Features/Standard-Library/Unicode-Support.md new file mode 100644 index 0000000..7a000f8 --- /dev/null +++ b/docs/Features/Standard-Library/Unicode-Support.md @@ -0,0 +1,25 @@ +--- +title: Unicode Support +parent: Standard Library +nav_order: 1 +permalink: /Features/Standard-Library/Unicode-Support +--- + +# Unicode Support + +twinBASIC provides full Unicode support throughout the language and runtime. + +## Native Unicode Functions + +Native functions that take string arguments, such as `MsgBox` and FileSystem functions (e.g. `Open`, `Dir`, `Mkdir`, `Kill`, and `RmDir`) now support Unicode. Additionally, .twin files make this easy to use as the editor supports Unicode as well. So you can paste a Unicode string in the editor, see it appear correctly, then have the same string correctly displayed by tB functions and controls. + +## Unicode in Controls + +All tB-implemented controls support Unicode, both in the code editor and when displayed. + +> [!IMPORTANT] +> If you subclass controls, note that this means you will receive the Unicode (W) version of window messages, e.g. ListViews will send `LVN_GETDISPINFOW (LVN_FIRST - 77)` instead of `LVN_GETDISPINFOA (LVN_FIRST - 50)`. + +## String Conversion + +`StrConv()` now has `vbUTF8` / `vbFromUTF8` options for UTF-8 string conversion. diff --git a/docs/Features/Standard-Library/index.md b/docs/Features/Standard-Library/index.md new file mode 100644 index 0000000..0e0c652 --- /dev/null +++ b/docs/Features/Standard-Library/index.md @@ -0,0 +1,17 @@ +--- +title: Standard Library +parent: Features +nav_order: 4 +permalink: /Features/Standard-Library/ +has_toc: false +--- + +# Standard Library Enhancements + +twinBASIC enhances the standard library with Unicode support, new functions, and improved file I/O capabilities. + +## Topics + +- [Unicode Support](Unicode-Support) - Native Unicode and encoding options +- [File I/O](File-IO) - Enhanced file operations with encoding support +- [New Functions](New-Functions) - New built-in functions and App object properties diff --git a/docs/Features/index.md b/docs/Features/index.md index ef6c762..2b3dc33 100644 --- a/docs/Features/index.md +++ b/docs/Features/index.md @@ -1,7 +1,90 @@ --- title: Features nav_order: 6 -permalink: /Features +permalink: /Features/ +has_toc: false --- -# Features \ No newline at end of file +# Features + +This section documents all the features and enhancements that twinBASIC brings compared to VBx and earlier BASIC dialects. + +twinBASIC maintains backward compatibility with VBx syntax while providing these powerful new features. Most enhancements are opt-in, allowing you to gradually adopt them in your projects. + +For detailed documentation on each feature, navigate to the specific category listed below. + +## Categories + +### [Attributes](Attributes-Intro) + +Attributes allow you to annotate Forms, Modules, Classes, Types, Enums, Declares, and procedures with compiler instructions and metadata. These are now visible directly in your code editor. + +### [Language Syntax](Language/) + +twinBASIC introduces numerous language enhancements including: + +- New data types: **LongPtr**, **LongLong**, **Decimal**, +- Native **Interface** and **CoClass** definitions, +- OOP features with **Implements Via** and **Inherits,** +- Generics and method overloading, +- Enhanced operators and literals, +- Type inference and pointer functionality, +- UDT enhancements with methods and events. + +### [Project Configuration](Project-Configuration/) + +twinBASIC offers various project types and configuration options: + +- Standard DLLs, Console applications, Services, and Kernel drivers +- Compiler options for optimization and security +- Entry point override and IAT placement +- Registration options for ActiveX projects + +### [Standard Library](Standard-Library) + +Enhancements to the standard library include: + +- Full Unicode support throughout +- File I/O with multiple encoding options +- New built-in functions and App object properties +- Direct COM error handling access +- Destructuring assignment for arrays + +### [GUI Components](GUI-Components/) + +Modernized GUI components featuring: + +- Enhanced forms with transparency and alpha blending +- Control anchoring and docking +- Windowed and windowless controls +- 64-bit support and DPI awareness +- New controls (QR Code, Multiframe, CheckMark) + +### [Packages](Packages/) + +Packages are collections of components that can be referenced from another twinBASIC project. They are distributed as TWINPACK files that contains everything needed by the components in that package. + +### [Advanced Features](Advanced/) + +Advanced programming capabilities: + +- Multithreading support via direct API calls +- Direct assembly insertion with `Emit()` +- Static linking of OBJ and LIB files +- Enhanced API declarations (CDecl, variadic args, ByVal UDTs) +- Parameterized constructors and class exports + +### [Compiler and IDE Features](Compiler-IDE/) + +Improved development experience: + +- Compiler warnings and strict mode +- Debug trace logger and stale pointer detection +- CodeLens for running Subs directly +- Modern IDE with themes, code folding, and more +- Package server for code sharing + +### [64bit Compilation](64bit) + +twinBASIC can compile native 64bit executables in addition to 32bit, using the **LongPtr** data type and **PtrSafe** keyword for API declarations. + diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index aee0898..9f138f9 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -34,6 +34,7 @@ GEM logger faraday-net_http (3.4.2) net-http (~> 0.5) + ffi (1.17.2) ffi (1.17.2-x64-mingw-ucrt) ffi (1.17.2-x86_64-linux-gnu) fiber-annotation (0.2.0) @@ -120,8 +121,12 @@ GEM logger (1.7.0) mercenary (0.4.0) metrics (0.15.0) + mini_portile2 (2.8.9) net-http (0.9.1) uri (>= 0.11.1) + nokogiri (1.18.10) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) nokogiri (1.18.10-x64-mingw-ucrt) racc (~> 1.4) nokogiri (1.18.10-x86_64-linux-gnu) @@ -218,6 +223,7 @@ CHECKSUMS eventmachine (1.2.7) sha256=994016e42aa041477ba9cff45cbe50de2047f25dd418eba003e84f0d16560972 faraday (2.14.0) sha256=8699cfe5d97e55268f2596f9a9d5a43736808a943714e3d9a53e6110593941cd faraday-net_http (3.4.2) sha256=f147758260d3526939bf57ecf911682f94926a3666502e24c69992765875906c + ffi (1.17.2) sha256=297235842e5947cc3036ebe64077584bff583cd7a4e94e9a02fdec399ef46da6 ffi (1.17.2-x64-mingw-ucrt) sha256=15d2da54ee578657a333a6059ed16eaba1cbd794ceecd15944825b65c8381ac0 ffi (1.17.2-x86_64-linux-gnu) sha256=05d2026fc9dbb7cfd21a5934559f16293815b7ce0314846fee2ac8efbdb823ea fiber-annotation (0.2.0) sha256=7abfadf1d119f508867d4103bf231c0354d019cc39a5738945dec2edadaf6c03 @@ -252,7 +258,9 @@ CHECKSUMS logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 mercenary (0.4.0) sha256=b25a1e4a59adca88665e08e24acf0af30da5b5d859f7d8f38fba52c28f405138 metrics (0.15.0) sha256=61ded5bac95118e995b1bc9ed4a5f19bc9814928a312a85b200abbdac9039072 + mini_portile2 (2.8.9) sha256=0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289 net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996 + nokogiri (1.18.10) sha256=d5cc0731008aa3b3a87b361203ea3d19b2069628cb55e46ac7d84a0445e69cc1 nokogiri (1.18.10-x64-mingw-ucrt) sha256=64f40d4a41af9f7f83a4e236ad0cf8cca621b97e31f727b1bebdae565a653104 nokogiri (1.18.10-x86_64-linux-gnu) sha256=ff5ba26ba2dbce5c04b9ea200777fd225061d7a3930548806f31db907e500f72 octicons (19.21.1) sha256=52f7330d7896fd18521999d1f6e9191f8b85ab22268b77e9f6d983a988556485 diff --git a/docs/Packages/index.md b/docs/Packages/index.md deleted file mode 100644 index ab0ccc9..0000000 --- a/docs/Packages/index.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: Packages -nav_order: 4 -permalink: /Packages ---- - -# Packages - diff --git a/docs/Reference/Attributes.md b/docs/Reference/Attributes.md index a82a608..b462df6 100644 --- a/docs/Reference/Attributes.md +++ b/docs/Reference/Attributes.md @@ -96,7 +96,7 @@ Public CoClass Foo End CoClass ``` -For an overview of coclasses in tB, see [Defining coclasses](../../Features/Overview#defining-coclasses). +For an overview of coclasses in tB, see [Defining coclasses](../../Features/Language/Interfaces-CoClasses#defining-coclasses). ## CoClassId (String) {: #coclassid } @@ -119,7 +119,7 @@ End CoClass The methods are [procedures](../Gloss#procedure). -For an overview of coclasses in tB, see [Defining coclasses](../../Features/Overview#defining-coclasses). +For an overview of coclasses in tB, see [Defining coclasses](../../Features/Language/Interfaces-CoClasses#defining-coclasses). ## COMControl (optional Bool) {: #comcontrol } @@ -378,7 +378,7 @@ End Interface The methods are [procedures](../Gloss#procedure). -For an overview of interfaces in tB, see [Defining interfaces](../../Features/Overview#defining-interfaces). +For an overview of interfaces in tB, see [Defining interfaces](../../Features/Language/Interfaces-CoClasses.html#defining-interfaces). ## MustBeQualified (optional Bool) {: #mustbequalified } @@ -403,6 +403,26 @@ Syntax: **[PackingAlignment( 1** \| **2** \| **4** \| **8** \| **16** \| **32** Applicable to: [**Type** (UDT)](Type) +twinBASIC normally aligns objects naturally within UDTs, e.g. an 8-byte object is aligned at the 8-byte boundary relative to the beginning of the UDT. This can leave gaps between UDT fields. A tighter packing can be achieved with a smaller **PackingAlignment**: + +```vb +[PackingAlignment(2)] +Private Type MyUDT + x As Integer + y As Long + z As Integer +End Type +Private t As MyUDT +Debug.Assert Len(t) = 8 And LenB(t) = 8 +``` + +You'll now find that both `Len(t)` and `LenB(t)` are 8. + +> [!NOTE] +> Alignment, not packing alignment, is not set this way. Specifying 16 would not get you a 16-byte structure for `t`. twinBASIC does not currently have an equivalent for `__declspec_align(n)`, but such a feature is planned. This is rare outside kernel mode programming. + +For introduction to this feature, see [Custom UDT Packing](../../Features/Language/UDTs#custom-udt-packing). + ## PopulateFrom (...) {: #populatefrom } diff --git a/docs/index.md b/docs/index.md index 2bb5984..67244be 100644 --- a/docs/index.md +++ b/docs/index.md @@ -29,22 +29,22 @@ The [twinBASIC wiki](https://github.com/twinbasic/documentation/wiki) supplement 4. [Painting / drawing to your control](CustomControls/Painting) 5. [Notes about the form designer](CustomControls/Form Designer) -## Packages -1. [What is a package?](Packages/What-Is) -2. [Creating a TWINPACK package](Packages/Creating-TWINPACK) -3. [Importing a package from TWINSERV](Packages/Importing-TWINSERV) -4. [Importing a package from a TWINPACK file](Packages/Importing-TWINPACK) -5. [Updating a package](Packages/Updating) - ## WebView2 1. [Getting Started](WebView2/Getting-Started) 2. [Customize the UserDataFolder](WebView2/Customize-UserDataFolder) 3. [Information about re-entrancy](WebView2/Re-entrancy) ## Language and IDE Features -1. [Overview of features new to twinBASIC](Features/Overview) (compared to VBx) -2. [Control anchoring and docking ‐ Automatic size and position management](Features/Anchoring-Docking) -3. [Windowless vs. Windowed Controls in VBx and twinBASIC](Features/Windowless) +1. [Overview of features new to twinBASIC](Features) (compared to VBx) +2. [Attributes](Features/Attributes-Intro) +3. [Language Syntax](Features/Language) +4. [Project Configuration](Features/Project-Configuration) +5. [Standard Library](Features/Standard-Library) +6. [GUI Components](Features/GUI-Components) +7. [Packages](Features/Packages) +8. [Advanced Features](Features/Advanced) +9. [Compiler and IDE Features](Features/Compiler-IDE) +10. [64bit Compilation](Features/64bit) ## Reference Section