From 1adce9eb943c067e4024ac6ad34f550bed93c1cf Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Mon, 11 May 2026 15:09:48 +0200 Subject: [PATCH 1/9] Document DriveListBox. --- docs/Reference/VB/DriveListBox/index.md | 449 ++++++++++++++++++++++++ docs/Reference/VB/todo.md | 11 - 2 files changed, 449 insertions(+), 11 deletions(-) create mode 100644 docs/Reference/VB/DriveListBox/index.md delete mode 100644 docs/Reference/VB/todo.md diff --git a/docs/Reference/VB/DriveListBox/index.md b/docs/Reference/VB/DriveListBox/index.md new file mode 100644 index 0000000..ee82fb9 --- /dev/null +++ b/docs/Reference/VB/DriveListBox/index.md @@ -0,0 +1,449 @@ +--- +title: DriveListBox +parent: VB Package +permalink: /tB/Packages/VB/DriveListBox/ +has_toc: false +--- + +# DriveListBox class +{: .no_toc } + +A **DriveListBox** is a Win32 native drop-down combo control that auto-populates with the drives reported by the operating system. The user picks one from the list; code reads the chosen drive through [**Drive**](#drive) and typically forwards it to a [**DirListBox**](../DirListBox) (whose [**Path**](../DirListBox/#path) it can be assigned to directly) to build a file picker alongside a [**FileListBox**](../FileListBox). The control is normally placed on a **Form** or **UserControl** at design time. The default property is [**Drive**](#drive) and the default event is [**Change**](#change). + +```tb +Private Sub Form_Load() + Drive1.Drive = "C:" + Dir1.Path = Drive1.Drive + File1.Path = Dir1.Path +End Sub + +Private Sub Drive1_Change() + Dir1.Path = Drive1.Drive +End Sub +``` + +* TOC +{:toc} + +## Drive list + +The list is populated automatically when the underlying window is created, by asking the OS for every currently-attached drive. Each entry combines the drive letter with the volume label, or — for a network drive — the UNC path the drive is mapped to: + +| Entry shape | Meaning | +|----------------------|------------------------------------------------------------------| +| `c: [Windows]` | Fixed or removable disk; volume label in brackets. | +| `d:` (no brackets) | Drive present but no volume label (unformatted, or empty CD-ROM).| +| `z: [\\srv\share]` | Network drive; UNC path in brackets. | + +Each entry is owner-drawn with an icon chosen from the drive type — closed disk, removable, fixed, CD-ROM, network, or RAM disk. The list cannot be edited from code: [**AddItem**](../ComboBox/#additem), [**RemoveItem**](../ComboBox/#removeitem), and [**Clear**](../ComboBox/#clear) are present in the type library for VB6 source compatibility but raise run-time error 438 (*Object doesn't support this property or method*) when called. Call [**Refresh**](#refresh) to re-read the drive set from the OS — useful after a removable medium is inserted or a network drive is mapped. + +[**ListCount**](#listcount) is the number of entries, [**List**](#list) returns the text of any entry by zero-based index, and [**TopIndex**](#topindex) controls vertical scrolling within the drop-down portion when it is open. [**NewIndex**](#newindex) reports the position of the last entry added during population (useful only when re-reading the list from code). + +## Drive property semantics + +Reading [**Drive**](#drive) returns the *displayed text* of the currently selected entry — drive letter, colon, and (where applicable) the bracketed volume label or UNC path, exactly as shown in the combo. + +Assigning to [**Drive**](#drive) looks only at the **first character** of the value and selects the entry whose drive letter matches (case-insensitively, by prefix). Anything after the first character is ignored, so `"C"`, `"C:"`, and `"C:\Windows"` all select the **C:** drive. If no entry matches the letter — e.g. when the requested drive is not currently attached — the assignment is silently ignored, leaving the previous selection in place. Assigning a value that matches the current selection does not raise [**Change**](#change); assigning a different value does. + +```tb +Drive1.Drive = "D" ' select drive D if present, else no-op +Debug.Print Drive1.Drive ' "d: [Backup]" (the displayed text) +``` + +## OLE drag and drop + +[**OLEDropMode**](#oledropmode) lets the control act as a drop target (restricted to **vbOLEDropNone** or **vbOLEDropManual**). Source-side automatic OLE drag is not supported on this control — VB6's `OLEDragMode` property was non-functional here and is omitted in twinBASIC. Call [**OLEDrag**](#oledrag) from code if a manual drag is needed. + +## Properties + +### Appearance +{: .no_toc } + +Determines how the control's border is drawn by the OS. A member of [**AppearanceConstants**](../../VBRUN/Constants/AppearanceConstants): **vbAppearFlat** or **vbAppear3d** (default). + +### BackColor +{: .no_toc } + +The background colour of the drop-down list entries, as an **OLE_COLOR**. Defaults to the system window-background colour. Selected entries paint with the system highlight colour regardless of this setting. Changing this calls [**Refresh**](#refresh) so the new colour takes effect immediately. + +### CausesValidation +{: .no_toc } + +Determines whether the previously focused control's [**Validate**](#validate) event runs before this control receives the focus. **Boolean**, default **True**. + +### ControlType +{: .no_toc } + +A read-only [**ControlTypeConstants**](../../VBRUN/Constants/ControlTypeConstants) value identifying this control as a drive list box. Always **vbDriveListBox**. + +### DragIcon +{: .no_toc } + +A **StdPicture** used as the mouse cursor while the control is being drag-and-dropped (see [**Drag**](#drag) and [**DragMode**](#dragmode)). + +### DragMode +{: .no_toc } + +Whether the control should drag itself when the user holds the mouse over it. A member of [**DragModeConstants**](../../VBRUN/Constants/DragModeConstants): **vbManual** (0, default — call [**Drag**](#drag) from code) or **vbAutomatic** (1). + +### Drive +{: .no_toc } + +The currently selected drive. **Default property.** + +Syntax: *object*.**Drive** [ = *string* ] + +Reading returns the displayed text of the selected entry — drive letter, colon, and (where applicable) the bracketed volume label or UNC path. Writing examines only the first character of *string* and selects the entry whose drive letter matches; values that do not match any present drive are silently ignored. Assigning a value that changes the selection raises [**Change**](#change). See [Drive property semantics](#drive-property-semantics) above for details. + +### Enabled +{: .no_toc } + +Determines whether the control accepts user input. A disabled drive list box still shows its current selection but is dimmed and ignores keyboard and mouse interaction. **Boolean**, default **True**. + +### Font +{: .no_toc } + +The **StdFont** used to render the drive entries. The convenience properties **FontName**, **FontSize**, **FontBold**, **FontItalic**, **FontStrikethru**, and **FontUnderline** read or write the corresponding members of this object. + +### ForeColor +{: .no_toc } + +The text colour for entries that are not currently selected, as an **OLE_COLOR**. Defaults to the system window-text colour. Disabled entries draw in the system grey-text colour, and selected entries draw in the system highlight-text colour, regardless of this setting. Changing this calls [**Refresh**](#refresh). + +### Height +{: .no_toc } + +The control's height when the drop-down is closed, in twips by default (or in the container's **ScaleMode** units). **Single**. The drop-down portion is sized by the OS. + +### HelpContextID +{: .no_toc } + +A **Long** identifying a topic in the application's help file, retrieved when the user presses **F1** while the control has focus. + +### hWnd +{: .no_toc } + +The Win32 window handle for the underlying combo box, as a **LongPtr**. Read-only. Useful for passing to API functions. + +### Index +{: .no_toc } + +When the control is part of a control array, the **Long** zero-based index of this instance within the array. Read-only at run time. + +### Left +{: .no_toc } + +The horizontal distance from the left edge of the container to the left edge of the control. **Single**. + +### List +{: .no_toc } + +The displayed text of the drive entry at the given index. Read-only. + +Syntax: *object*.**List**( *Index* ) + +*Index* +: *required* A **Long** zero-based item position, from `0` to `ListCount - 1`. + +### ListCount +{: .no_toc } + +The number of drive entries currently in the list, as a **Long**. Read-only. + +### ListIndex +{: .no_toc } + +The zero-based index of the selected entry, or `-1` if nothing is selected. **Long**. Assigning a value that differs from the current one selects that entry and raises [**Change**](#change). + +### MouseIcon +{: .no_toc } + +A **StdPicture** used as the mouse cursor when [**MousePointer**](#mousepointer) is **vbCustom** and the pointer is over the control. + +### MousePointer +{: .no_toc } + +The mouse cursor shown when the pointer is over the control. A member of [**MousePointerConstants**](../../VBRUN/Constants/MousePointerConstants). + +### Name +{: .no_toc } + +The unique design-time name of the control on its parent form. Read-only at run time. + +### NewIndex +{: .no_toc } + +The zero-based index at which the most recent list-population step inserted an entry, or `-1` if the list is empty. **Long**. Updated while the list is being filled (during [**Initialize**](#initialize) and after [**Refresh**](#refresh)); rarely useful at run time but read by some VB6-compatibility code. + +### OLEDropMode +{: .no_toc } + +How the control responds to OLE drops. A restricted member of [**OLEDropConstants**](../../VBRUN/Constants/OLEDropConstants): **vbOLEDropNone** or **vbOLEDropManual**. Automatic-drop mode is not supported on a DriveListBox. + +### Opacity +{: .no_toc } + +The control's opacity as a percentage (0–100, default 100). Values outside the range are clamped on **Initialize**. Requires Windows 8 or later for child controls. + +### Parent +{: .no_toc } + +A reference to the **Form** (or **UserControl**) that contains this control. Read-only. + +### TabIndex +{: .no_toc } + +The position of the control in the form's TAB-key navigation order. **Long**. + +### TabStop +{: .no_toc } + +Whether the user can reach the control by pressing the **TAB** key. **Boolean**, default **True**. A disabled control is skipped regardless of this setting. + +### Tag +{: .no_toc } + +A free-form **String** the application can use to associate custom data with the control. Ignored by the framework. + +### ToolTipText +{: .no_toc } + +A multi-line **String** displayed as a tooltip when the user hovers over the control. + +### Top +{: .no_toc } + +The vertical distance from the top of the container to the top of the control. **Single**. + +### TopIndex +{: .no_toc } + +The zero-based index of the entry shown at the top of the drop-down portion. Assigning a value scrolls the list so that entry is at the top, and raises [**Scroll**](#scroll) when the value actually changes. **Long**. + +### TransparencyKey +{: .no_toc } + +An **OLE_COLOR** that, when set, becomes fully transparent in the rendered control. Default `-1` disables the effect. Requires Windows 8 or later for child controls. + +### Visible +{: .no_toc } + +Whether the control is shown. **Boolean**, default **True**. + +### VisualStyles +{: .no_toc } + +Whether the OS theme engine should be used when drawing the control. **Boolean**. + +### WhatsThisHelpID +{: .no_toc } + +A **Long** identifying a "What's This?" help-pop-up topic in the application's help file. See [**ShowWhatsThis**](#showwhatsthis). + +### WheelScrollEvent +{: .no_toc } + +When **True** (default), mouse-wheel notifications over the drop-down list raise the [**Scroll**](#scroll) event; when **False**, the wheel still scrolls the list but [**Scroll**](#scroll) is suppressed. **Boolean**. VB6 never raised **Scroll** for wheel events; set this to **False** to match that behaviour exactly. + +### Width +{: .no_toc } + +The control's width. **Single**. + +## Methods + +### Drag +{: .no_toc } + +Begins, completes, or cancels a manual drag-and-drop operation when [**DragMode**](#dragmode) is **vbManual**. DriveListBox does not raise mouse events itself, so the call typically lives in a parent **Form** or container's mouse handler. + +Syntax: *object*.**Drag** [ *Action* ] + +*Action* +: *optional* A member of [**DragConstants**](../../VBRUN/Constants/DragConstants): **vbCancel** (0), **vbBeginDrag** (1, default), or **vbEndDrag** (2). + +### Move +{: .no_toc } + +Repositions and optionally resizes the control in a single call. + +Syntax: *object*.**Move** *Left* [, *Top* [, *Width* [, *Height* ] ] ] + +*Left* +: *required* A **Single** giving the new horizontal position. + +*Top*, *Width*, *Height* +: *optional* New values for the corresponding properties. Omitted values are left unchanged. + +### OLEDrag +{: .no_toc } + +Initiates an OLE drag operation from the control, raising the [**OLEStartDrag**](#olestartdrag) event so the application can populate the **DataObject**. + +Syntax: *object*.**OLEDrag** + +### Refresh +{: .no_toc } + +Re-reads the set of currently-attached drives from the operating system and repopulates the list, then redraws the control. Useful after a removable medium is inserted or a network drive is mapped or disconnected — the control does not watch for these events on its own. Does not raise [**Change**](#change), even if the previously-selected drive is no longer present (the selection moves to entry `0`). + +Syntax: *object*.**Refresh** + +### SetFocus +{: .no_toc } + +Moves the input focus to the control. The control must be both [**Visible**](#visible) and [**Enabled**](#enabled), or run-time error 5 (*Invalid procedure call or argument*) is raised. + +Syntax: *object*.**SetFocus** + +### ShowWhatsThis +{: .no_toc } + +Displays the topic identified by [**WhatsThisHelpID**](#whatsthishelpid) as a "What's This?" pop-up. + +Syntax: *object*.**ShowWhatsThis** + +### ZOrder +{: .no_toc } + +Brings the control to the front or back of its sibling stack. + +Syntax: *object*.**ZOrder** [ *Position* ] + +*Position* +: *optional* A member of [**ZOrderConstants**](../../VBRUN/Constants/ZOrderConstants): **vbBringToFront** (0, default) or **vbSendToBack** (1). + +## Events + +### Change +{: .no_toc } + +Raised after the selected drive changes — whether the user picked a different entry from the drop-down or code assigned a different value to [**Drive**](#drive) or [**ListIndex**](#listindex). Not raised for assignments that match the current selection, nor during [**Refresh**](#refresh) or the initial population that occurs before [**Initialize**](#initialize). **Default event.** + +Syntax: *object*\_**Change**( ) + +### CloseUp +{: .no_toc } + +Raised when the drop-down portion closes — either because the user picked an entry, clicked elsewhere, or pressed **Esc**. + +Syntax: *object*\_**CloseUp**( ) + +### DragDrop +{: .no_toc } + +Raised on the destination control when a manual drag operation ends over it. + +Syntax: *object*\_**DragDrop**( *Source* **As Control**, *X* **As Single**, *Y* **As Single** ) + +### DragOver +{: .no_toc } + +Raised on the control under the cursor while a manual drag operation is in progress. + +Syntax: *object*\_**DragOver**( *Source* **As Control**, *X* **As Single**, *Y* **As Single**, *State* **As Integer** ) + +### DropDown +{: .no_toc } + +Raised when the user opens the drop-down portion. + +Syntax: *object*\_**DropDown**( ) + +### GotFocus +{: .no_toc } + +Raised when the control receives the input focus. + +Syntax: *object*\_**GotFocus**( ) + +### Initialize +{: .no_toc } + +Raised once, immediately after the underlying window is created and the initial list of drives has been loaded. New in twinBASIC — VB6 had no equivalent on this control. + +Syntax: *object*\_**Initialize**( ) + +### KeyDown +{: .no_toc } + +Raised when the user presses any key while the control has focus. + +Syntax: *object*\_**KeyDown**( *KeyCode* **As Integer**, *Shift* **As Integer** ) + +### KeyPress +{: .no_toc } + +Raised when the user types a character that produces an ANSI keystroke. + +Syntax: *object*\_**KeyPress**( *KeyAscii* **As Integer** ) + +### KeyUp +{: .no_toc } + +Raised when the user releases a key while the control has focus. + +Syntax: *object*\_**KeyUp**( *KeyCode* **As Integer**, *Shift* **As Integer** ) + +### LostFocus +{: .no_toc } + +Raised when the control loses the input focus. + +Syntax: *object*\_**LostFocus**( ) + +### OLECompleteDrag +{: .no_toc } + +Raised on the source control when the OLE drag operation finishes, indicating which effect (copy, move, none) the destination accepted. + +Syntax: *object*\_**OLECompleteDrag**( *Effect* **As Long** ) + +### OLEDragDrop +{: .no_toc } + +Raised on the destination control when the user drops data on it. + +Syntax: *object*\_**OLEDragDrop**( *Data* **As DataObject**, *Effect* **As Long**, *Button* **As Integer**, *Shift* **As Integer**, *X* **As Single**, *Y* **As Single** ) + +### OLEDragOver +{: .no_toc } + +Raised on the destination control while an OLE drag passes over it. + +Syntax: *object*\_**OLEDragOver**( *Data* **As DataObject**, *Effect* **As Long**, *Button* **As Integer**, *Shift* **As Integer**, *X* **As Single**, *Y* **As Single**, *State* **As Integer** ) + +### OLEGiveFeedback +{: .no_toc } + +Raised on the source control during a drag so the application can adjust the cursor or other visual feedback. + +Syntax: *object*\_**OLEGiveFeedback**( *Effect* **As Long**, *DefaultCursors* **As Boolean** ) + +### OLESetData +{: .no_toc } + +Raised on the source control when the destination requests data in a format that was registered but not yet supplied. + +Syntax: *object*\_**OLESetData**( *Data* **As DataObject**, *DataFormat* **As Integer** ) + +### OLEStartDrag +{: .no_toc } + +Raised on the source control at the start of an OLE drag, so the application can populate the **DataObject** and choose the allowed effects. + +Syntax: *object*\_**OLEStartDrag**( *Data* **As DataObject**, *AllowedEffects* **As Long** ) + +### Scroll +{: .no_toc } + +Raised when the drop-down list is scrolled — by the scroll bar, the keyboard, or (when [**WheelScrollEvent**](#wheelscrollevent) is **True**) the mouse wheel. The new offset can be read from [**TopIndex**](#topindex). + +Syntax: *object*\_**Scroll**( ) + +### Validate +{: .no_toc } + +Raised when the focus is moving to another control whose [**CausesValidation**](#causesvalidation) is **True**. Setting *Cancel* to **True** keeps the focus on this control. + +Syntax: *object*\_**Validate**( *Cancel* **As Boolean** ) diff --git a/docs/Reference/VB/todo.md b/docs/Reference/VB/todo.md deleted file mode 100644 index 4949fca..0000000 --- a/docs/Reference/VB/todo.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: General TODO List for /tB/Packages/VB/ -nav_exclude: true -redirect_from: - - /tB/Packages/VB/DriveListBox ---- - -> [!WARNING] -> -> Pardon, we have not documented this class yet. - From e8e5f70f65d20afb2f2afbcef6dce8356d717d9c Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Mon, 11 May 2026 15:12:03 +0200 Subject: [PATCH 2/9] Document the Alias statement. --- docs/Reference/Core/Alias.md | 61 ++++++++++++++++++++++++++++++++++++ docs/Reference/Statements.md | 2 ++ 2 files changed, 63 insertions(+) create mode 100644 docs/Reference/Core/Alias.md diff --git a/docs/Reference/Core/Alias.md b/docs/Reference/Core/Alias.md new file mode 100644 index 0000000..21c83dd --- /dev/null +++ b/docs/Reference/Core/Alias.md @@ -0,0 +1,61 @@ +--- +title: Alias +parent: Statements +permalink: /tB/Core/Alias +--- +# Alias +{: .no_toc } + +Declares an alternative name for an intrinsic type, user-defined [**Type**](Type), [**Interface**](Interface), or another **Alias**. The alias and the original type are interchangeable — assigning between them is not a type mismatch. Comparable to `typedef` in C/C++. + +> [!NOTE] +> The **Alias** statement is a twinBASIC extension. It has no equivalent in classic VBA, where the only use of the **Alias** keyword is to name a DLL entry point in a [**Declare**](Declare) statement. + +Syntax: +> [ **Public** \| **Private** ] **Alias** *aliasname* **As** *type* + +**Public** +: *optional* The alias is exported to the type library of an ActiveX DLL or control, so consumers in other projects see *aliasname* itself. + +**Private** +: *optional* The alias is visible only within the project. Usages of a **Private** alias are replaced with the underlying *type* during compilation, so *aliasname* never appears in the project's type library. + +*aliasname* +: The name of the alias. Must be a valid twinBASIC identifier. + +*type* +: The original type. May be an intrinsic type, a user-defined [**Type**](Type), an [**Interface**](Interface), or another **Alias**. + +**Alias** statements are valid only in `.twin` source files (not legacy `.bas` or `.cls` files), and must appear at file scope — outside of [**Module**](Module) and [**Class**](Class) blocks, alongside [**Interface**](Interface) and [**CoClass**](CoClass) declarations. + +### Example + +Aliasing intrinsic types and a user-defined type: + +```tb +Public Type POINT + x As Long + y As Long +End Type + +Public Alias POINTAPI As POINT + +Public Alias CBoolean As Byte + +Public Alias KAFFINITY As LongPtr +``` + +A variable declared with the alias and a variable declared with the original type are interchangeable: + +```tb +Dim p As POINT +Dim q As POINTAPI +p = q ' OK — no type mismatch. +``` + +### See Also + +- [**Type** statement](Type) +- [**Interface** statement](Interface) +- [**CoClass** statement](CoClass) +- [Alias Types](../../Features/Language/Alias-Types) diff --git a/docs/Reference/Statements.md b/docs/Reference/Statements.md index 7c2f5e3..dc4990c 100644 --- a/docs/Reference/Statements.md +++ b/docs/Reference/Statements.md @@ -12,6 +12,8 @@ These statements are built into the language itself. They are understood by the ## Alphabetical List +* [Alias](../tB/Core/Alias) -- (twinBASIC) declares an alternative name for an intrinsic type, user-defined type, or interface + * [Call](../tB/Core/Call) -- transfer control to a procedure * [Class](../tB/Core/Class) -- define a class From 856970e726b6c0b72978c672c64b7766f86d0b3d Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Mon, 11 May 2026 17:08:47 +0200 Subject: [PATCH 3/9] Document tB specifics in Class, Continue, and Dim. --- docs/Reference/Core/Class.md | 35 +++++++++++++++++++++++++++++---- docs/Reference/Core/Continue.md | 3 +++ docs/Reference/Core/Dim.md | 8 ++++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/docs/Reference/Core/Class.md b/docs/Reference/Core/Class.md index 75e943d..4e81716 100644 --- a/docs/Reference/Core/Class.md +++ b/docs/Reference/Core/Class.md @@ -13,7 +13,8 @@ Used to define a class. Classes are templates from which objects are created -- Syntax: > [ *attributes* ] -> **Class** *name* +> [ **Public** \| **Private** ] **Class** *name* [ **(** **Of** *typevars* **)** ] +>     [ **Inherits** *baseclass* ] >     [ *classmember* ] >     [ *classmember* ] ... > **End Class** @@ -22,14 +23,40 @@ Syntax: : *optional* One or more of: [ArrayBoundsChecks](Attributes#arrayboundschecks), [ClassId](Attributes#classid), [COMCreatable](Attributes#comcreatable), [CustomControl](Attributes#customcontrol), [Description](Attributes#description), [FloatingPointErrorChecks](Attributes#floatingpointerrorchecks), [FormDesignerId](Attributes#formdesignerid), [Hidden](Attributes#hidden), [IntegerOverflowChecks](Attributes#integeroverflowchecks), [PredeclaredID](Attributes#predeclaredid) +**Public** +: *optional* (twinBASIC) In an ActiveX project, marks the class as exported into the type library so that consumers in other projects can create and use it. + +**Private** +: *optional* (twinBASIC) In an ActiveX project, withholds the class from the type library: it remains usable within the project but is not exported. The conventional pairing with [**CoClass**](CoClass) — a public **CoClass** as the consumer-visible contract paired with a `Private Class` as the hidden implementation — relies on this modifier. + *name* : The identifier naming the class. +**Of** *typevars* +: *optional* (twinBASIC) One or more type variable names, separated by commas, that make the class a *generic class*. Each type variable can be referenced in member declarations as if it were a regular type. See [Generics](../../Features/Language/Generics). + +**Inherits** *baseclass* +: *optional* (twinBASIC) Names a single base class whose **Public** and [**Protected**](Protected) members are inherited by *name*. The **Inherits** line, when present, must appear immediately after the **Class** header and before any other member. **Inherits** enables [**Overridable**](Sub) / **Overrides** members, explicit `*baseclass*.New(...)` chained constructor calls from inside `Sub New`, and **Protected** member visibility. See [Inheritance](../../Features/Language/Inheritance). + *classmember* : *optional* Any of the following: - [constant](../Gloss#constant) defined using [**Const**](Const), - - [variable](../Gloss#variable) defined using [**Public**](Public), [**Protected**](Protected), [**Private**](Private), and [**Dim**](Dim), - - [procedure](../Gloss#procedure) defined using [**Sub**](Sub), [**Function**](Function) and [**Property**](Property), - - [user-defined type (UDTs)](../Gloss#user-defined-type) defined using [**Type**](Type). + - [variable](../Gloss#variable) defined using [**Public**](Public), [**Protected**](Protected), [**Private**](Private), or [**Dim**](Dim), + - [procedure](../Gloss#procedure) defined using [**Sub**](Sub), [**Function**](Function), or [**Property**](Property) — including the special instance constructor `Sub New(`*args*`)`, which the runtime invokes when the class is created with [**New**](New), + - [user-defined type (UDTs)](../Gloss#user-defined-type) defined using [**Type**](Type), + - (twinBASIC) [**Implements**](Implements) clauses, listing interfaces or classes whose members this class provides bodies for. + +In `.twin` files, a **Class** block may share a file with [**Interface**](Interface), [**CoClass**](CoClass), and [**Alias**](Alias) declarations (which appear *before* the **Class** block) and with a [**Module**](Module) block. In legacy `.cls` files the class is implicit and the **Class**/**End Class** keywords are not written. + +### See Also + +- [**Module** statement](Module) +- [**Interface** statement](Interface) +- [**CoClass** statement](CoClass) +- [**Implements** statement](Implements) +- [**Protected** statement](Protected) +- [**New** statement](New) +- [Inheritance](../../Features/Language/Inheritance) +- [Generics](../../Features/Language/Generics) diff --git a/docs/Reference/Core/Continue.md b/docs/Reference/Core/Continue.md index f2e3e85..0504de6 100644 --- a/docs/Reference/Core/Continue.md +++ b/docs/Reference/Core/Continue.md @@ -24,6 +24,9 @@ While : Used within a [While](While-Wend) loop +> [!NOTE] +> **Continue** is a twinBASIC extension. Classic VBA has no skip-iteration form for any loop construct — the closest equivalent is a forward [**GoTo**](GoTo) to a label placed just before the loop's terminator. + ### Example This example uses **Continue For** to skip processing of certain characters of the string. diff --git a/docs/Reference/Core/Dim.md b/docs/Reference/Core/Dim.md index c90eedb..44c74ee 100644 --- a/docs/Reference/Core/Dim.md +++ b/docs/Reference/Core/Dim.md @@ -9,7 +9,7 @@ permalink: /tB/Core/Dim Declares variables and allocates storage space. -Syntax: **Dim** [ **WithEvents** ] *varname* [ **(** [ *subscripts* ] **)** ] [ **As** [ **New** ] *type* ] [ **,** [ **WithEvents** ] *varname* [ **(** [ *subscripts* ] **)** ] [ **As** [ **New** ] *type* ]] **. . .** +Syntax: **Dim** [ **WithEvents** ] *varname* [ **(** [ *subscripts* ] **)** ] [ **As** [ **New** ] *type* ] [ **=** *expression* ] [ **,** [ **WithEvents** ] *varname* [ **(** [ *subscripts* ] **)** ] [ **As** [ **New** ] *type* ] [ **=** *expression* ] ] **. . .** **WithEvents** @@ -29,7 +29,11 @@ Syntax: **Dim** [ **WithEvents** ] *varname* [ **(** [ *subscripts* ] **)** ] [ *type* -: *optional*. Data type of the variable; may be Byte, Boolean, Integer, Long, Currency, Single, Double, Decimal (not currently supported), Date, String (for variable-length strings), **String** *length* (for fixed-length strings), Object, Variant, a user-defined type (UDT), or an object type. Use a separate **As** *type* clause for each variable you declare. +: *optional*. Data type of the variable; may be **Byte**, **Boolean**, **Integer**, **Long**, **LongLong**, **LongPtr**, **Currency**, **Single**, **Double**, **Decimal**, **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, a user-defined type (UDT), an object type, or **Any** (twinBASIC; type is inferred from *expression* — see [Type Inference](../../Features/Language/Type-Inference)). Use a separate **As** *type* clause for each variable you declare. + +*expression* + +: *optional*. (twinBASIC) Initial value assigned to the variable at declaration. Equivalent to a separate assignment statement immediately after the **Dim** — `Dim i As Long = 1` is the same as `Dim i As Long: i = 1`. For object types, `= New *type* ( *args* )` constructs an instance (and may pass custom-constructor arguments). When *type* is **Any**, *expression* is required and determines the inferred type. See [Inline Variable Initialization](../../Features/Language/Inline-Initialization). Variables declared with **Dim** at the module level are available to all procedures within the module. At the procedure level, variables are available only within the procedure. From 616a7c560692e1b8936b0a61ae7583968b20bbed Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Mon, 11 May 2026 17:14:07 +0200 Subject: [PATCH 4/9] Document tB specifics in Function, Property, Sub and Type. --- docs/Reference/Core/Function.md | 15 +++++++- docs/Reference/Core/Property.md | 24 ++++++++++-- docs/Reference/Core/Sub.md | 22 ++++++++++- docs/Reference/Core/Type.md | 65 ++++++++++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 8 deletions(-) diff --git a/docs/Reference/Core/Function.md b/docs/Reference/Core/Function.md index 75d5ff7..4214ce5 100644 --- a/docs/Reference/Core/Function.md +++ b/docs/Reference/Core/Function.md @@ -10,7 +10,7 @@ Declares the name, arguments, and code that form the body of a **Function** proc Syntax: > [ *attributes* ] -> [ **Public** | **Private** | **Friend** ] [ **Static** ] **Function** *name* [ **(** **Of** *typevars* **)** ] [ **(** *arglist* **)** ] [ **As** *type* ] +> [ **Public** \| **Private** \| **Friend** \| **Protected** ] [ **Static** ] [ **Overridable** ] **Function** *name* [ **(** **Of** *typevars* **)** ] [ **(** *arglist* **)** ] [ **As** *type* ] [ *binding-clause* ] >      [ *statements* ] ... >      [ [ **Let** ] *name* **=** *expression* ] ... >      [ **Set** *name* **=** *expression* ] ... @@ -32,9 +32,15 @@ Syntax: **Friend** : *optional* Used only in a class module. Indicates that the **Function** procedure is visible throughout the project, but not visible to a controller of an instance of an object. +**[Protected](Protected)** +: *optional* (twinBASIC) Used only in a class. Indicates that the **Function** procedure is accessible from inside the declaring class and from classes that derive from it via [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop), but not from outside callers. + **Static** : *optional* Indicates that the **Function** procedure's local variables are preserved between calls. The **Static** attribute doesn't affect variables that are declared outside the **Function**, even if they are used in the procedure. +**Overridable** +: *optional* (twinBASIC) Marks the **Function** as an inheritance hook that classes derived via [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop) may replace with an **Overrides** clause. Meaningful only on a member of a class that participates in an **Inherits** hierarchy. + *name* : Name of the **Function**; follows standard variable naming conventions. @@ -47,6 +53,13 @@ Syntax: **As** *type* : *optional* Data type of the value returned by the **Function** procedure; may be Byte, Boolean, Integer, Long, Currency, Single, Double, Decimal (not currently supported), Date, String (except fixed length), Object, Variant, or any user-defined type (UDT). +*binding-clause* +: *optional* (twinBASIC) One of three trailing clauses that bind this body to a member declared elsewhere: + + - **Handles** *object*.*event* — wires this **Function** up as a handler for the named event, replacing the traditional `Object_Event` naming convention. See [Handler Method Syntax](../../Features/Language/Handlers). + - **Implements** *iface*.*member* [ **,** *iface2*.*member2* … ] — provides the body for the named [**Interface**](Interface) (or [**Class**](Class)) member, replacing the traditional `Iface_Member` naming convention. A comma-separated list lets one body satisfy several interfaces' members at once. See [**Implements** statement](Implements). + - **Overrides** *base*.*member* — supplies the body for an **Overridable** *member* inherited via [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop). Combine with **Overridable** on the same header to allow further-derived classes to override again. + *statements* : *optional* Any group of statements to be executed within the **Function** procedure. diff --git a/docs/Reference/Core/Property.md b/docs/Reference/Core/Property.md index b53bd00..298ff92 100644 --- a/docs/Reference/Core/Property.md +++ b/docs/Reference/Core/Property.md @@ -17,7 +17,7 @@ A property is exposed to callers through up to three property procedures, all sh Syntax: - > [ *attributes* ] - > [ **Public** \| **Private** \| **Friend** ] [ **Static** ] **Property Get** *name* [ **(** **Of** *typevars* **)** ] [ **(** *arglist* **)** ] [ **As** *type* ] + > [ **Public** \| **Private** \| **Friend** \| **Protected** ] [ **Static** ] [ **Overridable** ] **Property Get** *name* [ **(** **Of** *typevars* **)** ] [ **(** *arglist* **)** ] [ **As** *type* ] [ *binding-clause* ] >      [ *statements* ] ... >      [ [ **Let** ] *name* **=** *expression* ] ... >      [ **Set** *name* **=** *expression* ] ... @@ -27,14 +27,14 @@ Syntax: > **End Property** - > [ *attributes* ] - > [ **Public** \| **Private** \| **Friend** ] [ **Static** ] **Property Let** *name* [ **(** **Of** *typevars* **)** ] **(** [ *arglist* **,** ] *value* **)** + > [ **Public** \| **Private** \| **Friend** \| **Protected** ] [ **Static** ] [ **Overridable** ] **Property Let** *name* [ **(** **Of** *typevars* **)** ] **(** [ *arglist* **,** ] *value* **)** [ *binding-clause* ] >      [ *statements* ] ... >      [ **Exit Property** ] ... >      [ *statements* ] ... > **End Property** - > [ *attributes* ] - > [ **Public** \| **Private** \| **Friend** ] [ **Static** ] **Property Set** *name* [ **(** **Of** *typevars* **)** ] **(** [ *arglist* **,** ] *reference* **)** + > [ **Public** \| **Private** \| **Friend** \| **Protected** ] [ **Static** ] [ **Overridable** ] **Property Set** *name* [ **(** **Of** *typevars* **)** ] **(** [ *arglist* **,** ] *reference* **)** [ *binding-clause* ] >      [ *statements* ] ... >      [ **Exit Property** ] ... >      [ *statements* ] ... @@ -52,9 +52,15 @@ Syntax: **Friend** : *optional* Used only in a class module. Indicates that the **Property** procedure is visible throughout the project, but not visible to a controller of an instance of an object. +**[Protected](Protected)** +: *optional* (twinBASIC) Used only in a class. Indicates that the **Property** procedure is accessible from inside the declaring class and from classes that derive from it via [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop), but not from outside callers. All three accessor forms of the same property (**Get**, **Let**, **Set**) should agree on the access modifier. + **[Static](Static)** : *optional* Indicates that the **Property** procedure's local variables are preserved between calls. The **Static** attribute doesn't affect variables that are declared outside the **Property** procedure, even if they are used in the procedure. +**Overridable** +: *optional* (twinBASIC) Marks the **Property** as an inheritance hook that classes derived via [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop) may replace with an **Overrides** clause. Meaningful only on a member of a class that participates in an **Inherits** hierarchy. + *name* : Name of the **Property** procedure; follows standard variable naming conventions, except that the same name is shared by the matching **Property Get**, **Property Let**, and **Property Set** procedures in the same module. @@ -79,6 +85,13 @@ Syntax: *reference* : In **Property Set**, the variable containing the object reference used on the right side of the object reference assignment. *reference* cannot be **Optional**. +*binding-clause* +: *optional* (twinBASIC) One of three trailing clauses that bind this accessor to a member declared elsewhere: + + - **Handles** *object*.*event* — wires the property up as a handler for the named event, replacing the traditional `Object_Event` naming convention. See [Handler Method Syntax](../../Features/Language/Handlers). + - **Implements** *iface*.*member* [ **,** *iface2*.*member2* … ] — provides the body for the named [**Interface**](Interface) (or [**Class**](Class)) member, replacing the traditional `Iface_Member` naming convention. A comma-separated list lets one body satisfy several interfaces' members at once. See [**Implements** statement](Implements). + - **Overrides** *base*.*member* — supplies the body for an **Overridable** *member* inherited via [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop). Combine with **Overridable** on the same header to allow further-derived classes to override again. + **[Exit Property](Exit)** : *optional* Immediately returns from the **Property** procedure without setting a return value. Valid in **Property Get**, **Property Let**, and **Property Set**. @@ -177,5 +190,10 @@ End Property - [**Set** statement](Set) - [**Exit** statement](Exit) - [**Return** statement](Return) +- [**Implements** statement](Implements) +- [**Protected** statement](Protected) +- [Handler Method Syntax](../../Features/Language/Handlers) +- [Inheritance](../../Features/Language/Inheritance) +- [Generics](../../Features/Language/Generics) {% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Sub.md b/docs/Reference/Core/Sub.md index 6ada7b7..2dee00d 100644 --- a/docs/Reference/Core/Sub.md +++ b/docs/Reference/Core/Sub.md @@ -10,7 +10,7 @@ Declares the name, arguments, and code that form the body of a **Sub** procedure Syntax: > [ *attributes* ] -> [ **Public** \| **Private** \| **Friend** ] [ **Static** ] **Sub** *name* [ **(** **Of** *typevars* **)** ] [ **(** *arglist* **)** ] +> [ **Public** \| **Private** \| **Friend** \| **Protected** ] [ **Static** ] [ **Overridable** ] **Sub** *name* [ **(** **Of** *typevars* **)** ] [ **(** *arglist* **)** ] [ *binding-clause* ] >      [ *statements* ] ... >      [ **Exit Sub** ] ... >      [ *statements* ] ... @@ -28,11 +28,17 @@ Syntax: **Friend** : *optional* Used only in a class module. Indicates that the **Sub** procedure is visible throughout the project, but not visible to a controller of an instance of an object. +**[Protected](Protected)** +: *optional* (twinBASIC) Used only in a class. Indicates that the **Sub** procedure is accessible from inside the declaring class and from classes that derive from it via [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop), but not from outside callers. + **[Static](Static)** : *optional* Indicates that the **Sub** procedure's local variables are preserved between calls. The **Static** attribute doesn't affect variables that are declared outside the **Sub**, even if they are used in the procedure. +**Overridable** +: *optional* (twinBASIC) Marks the **Sub** as an inheritance hook that classes derived via [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop) may replace with an **Overrides** clause. Meaningful only on a member of a class that participates in an **Inherits** hierarchy. + *name* -: Name of the **Sub**; follows standard variable naming conventions. +: Name of the **Sub**; follows standard variable naming conventions. The special name `New` declares an instance constructor — see [Inheritance](../../Features/Language/Inheritance) for chained construction with `*baseclass*.New(...)`. **Of** *typevars* : *optional* One or more type variable names, following standard variable naming conventions. The names are separated by commas. Causes the procedure to be a generic **Sub**. @@ -40,6 +46,13 @@ Syntax: *arglist* : *optional* List of variables representing arguments that are passed to the **Sub** procedure when it is called. Multiple variables are separated by commas. See [*arglist*](#arglist) below for syntax. +*binding-clause* +: *optional* (twinBASIC) One of three trailing clauses that bind this body to a member declared elsewhere: + + - **Handles** *object*.*event* — wires this **Sub** up as a handler for the named event, replacing the traditional `Object_Event` naming convention. See [Handler Method Syntax](../../Features/Language/Handlers). + - **Implements** *iface*.*member* [ **,** *iface2*.*member2* … ] — provides the body for the named [**Interface**](Interface) (or [**Class**](Class)) member, replacing the traditional `Iface_Member` naming convention. A comma-separated list lets one body satisfy several interfaces' members at once. See [**Implements** statement](Implements). + - **Overrides** *base*.*member* — supplies the body for an **Overridable** *member* inherited via [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop). Combine with **Overridable** on the same header to allow further-derived classes to override again. + *statements* : *optional* Any group of statements to be executed within the **Sub** procedure. @@ -123,5 +136,10 @@ End Sub - [**Property** statement](Property) - [**Exit** statement](Exit) - [**Return** statement](Return) +- [**Implements** statement](Implements) +- [**Protected** statement](Protected) +- [Handler Method Syntax](../../Features/Language/Handlers) +- [Inheritance](../../Features/Language/Inheritance) +- [Generics](../../Features/Language/Generics) {% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Type.md b/docs/Reference/Core/Type.md index 824ce6b..abc938d 100644 --- a/docs/Reference/Core/Type.md +++ b/docs/Reference/Core/Type.md @@ -9,12 +9,18 @@ permalink: /tB/Core/Type Used at the module level to define a user-defined data type containing one or more elements. Syntax: -> [ **Private** \| **Public** ] **Type** *varname* +> [ *attributes* ] +> [ **Private** \| **Public** ] **Type** *varname* [ **(** **Of** *typevars* **)** ] >      *elementname* [ **(** [ *subscripts* ] **)** ] **As** *type* >      [ *elementname* [ **(** [ *subscripts* ] **)** ] **As** *type* ] >      **. . .** +>      [ *member-procedure* ] +>      **. . .** > **End Type** +*attributes* +: *optional* (twinBASIC) Type-level attributes. Most notably [**PackingAlignment**](Attributes#packingalignment), which sets the field-alignment value used when laying out the UDT in memory — useful for interop with C structs declared under `#pragma pack` or `#include `. + **Public** : *optional* Used to declare user-defined types that are available to all procedures in all modules in all projects. @@ -31,7 +37,13 @@ Syntax: : *optional* Dimensions of an array element. When not explicitly stated in *lower*, the lower bound of an array is controlled by the [**Option Base**](Option#Base) statement. The lower bound is zero if no **Option Base** statement is present. *type* -: Data type of the element; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, another user-defined type, or an object type. +: Data type of the element; may be **Byte**, **Boolean**, **Integer**, **Long**, **LongLong**, **LongPtr**, **Currency**, **Single**, **Double**, **Decimal**, **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, another user-defined type, or an object type. In a generic **Type** (see below), *type* may also be one of the *typevars* introduced in the **Of** clause. + +**Of** *typevars* +: *optional* (twinBASIC) One or more type variable names, separated by commas, that make the **Type** a *generic UDT*. Each type variable can be referenced as the *type* of an element. See [Generics](../../Features/Language/Generics). Generic UDTs do not yet support member procedures. + +*member-procedure* +: *optional* (twinBASIC) A [**Sub**](Sub), [**Function**](Function), or [**Property**](Property) procedure, or a [**Declare**](Declare) external procedure, written inside the **Type** body and callable through any variable of that type. See [twinBASIC enhancements](#twinbasic-enhancements) below. The **Type** statement can be used only at the module level. After you have declared a user-defined type by using the **Type** statement, you can declare a variable of that type anywhere within the scope of the declaration. Use [**Dim**](Dim), [**Private**](Private), [**Public**](Public), [**ReDim**](ReDim), or [**Static**](Static) to declare a variable of a user-defined type. @@ -77,6 +89,53 @@ Sub CreateRecord() End Sub ``` +### twinBASIC enhancements + +twinBASIC extends classic VBA's **Type** in several ways. UDTs remain stack-allocated structs that can be passed to Win32 APIs, but they can also carry behaviour like a lightweight class. + +**Member procedures.** [**Sub**](Sub), [**Function**](Function), and [**Property**](Property) procedures may appear inside `Type ... End Type`, and are called through any variable of the type. Inside a member procedure, other members of the same UDT must be accessed with the explicit `Me.` prefix. + +**Lifecycle and operator hooks.** Well-known member names are wired up by the compiler: + +| Member | Purpose | +|:---|:---| +| `Type_Initialize` | Constructor — runs when a variable of the type is created. | +| `Type_Terminate` | Destructor — runs when the variable goes out of scope. | +| `Type_Assignment` | Assignment operator — `=` assigns *RHS* to a variable of the type. The signature is `Sub Type_Assignment(ByVal RHS As ...)`, and several overloads with different *RHS* types may coexist. | +| `Type_Conversion` | Conversion operator — produces a value of another type from the UDT. The signature is `Function Type_Conversion() As ...`, and several overloads with different return types may coexist. | +| `Type_DebugView` | Debugger display — returns a **String** shown in the IDE's variable inspector. | + +**API declarations inside a UDT.** A [**Declare**](Declare) statement inside a **Type** body works as a regular module-level declaration, except that when its first parameter is named `Me` and is the same type as the UDT, calls on a variable of that type pass the variable as the first argument implicitly: + +```tb +Type HWND + Value As LongPtr + Public Declare PtrSafe Function BringWindowToTop Lib "user32" (ByVal Me As HWND) As Long +End Type + +Dim h As HWND +h.BringWindowToTop() ' Passes h as the first argument to the API. +``` + +**Custom packing.** The [**PackingAlignment**](Attributes#packingalignment) type-level attribute controls alignment of the UDT's fields. The default packing places each field at a multiple of its own size (with trailing padding so the total size is a multiple of the largest field). Setting `[PackingAlignment(1)]` packs fields with no padding — matching `#pragma pack(push, 1)` in C. + +```tb +[PackingAlignment(2)] +Private Type MyUDT + x As Integer + y As Long + z As Integer +End Type +``` + +**Generic types.** A type variable list `(Of T)` after the *varname* makes the **Type** generic. Element types may then reference the type variables. Member procedures are not yet supported on generic UDTs. + +```tb +Type ListU(Of T) + value() As T +End Type +``` + ### See Also - [**Dim** statement](Dim) @@ -84,5 +143,7 @@ End Sub - [**Public** statement](Public) - [**Enum** statement](Enum) - [**Class** statement](Class) +- [UDT Enhancements](../../Features/Language/UDTs) +- [Generics](../../Features/Language/Generics) {% include VBA-Attribution.md %} From 563930e769c0fb742566ccc9f5da1ae7488f619b Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Mon, 11 May 2026 17:18:38 +0200 Subject: [PATCH 5/9] Document Delegate. --- docs/Reference/Core/Delegate.md | 102 ++++++++++++++++++++++++++++++++ docs/Reference/Statements.md | 2 + 2 files changed, 104 insertions(+) create mode 100644 docs/Reference/Core/Delegate.md diff --git a/docs/Reference/Core/Delegate.md b/docs/Reference/Core/Delegate.md new file mode 100644 index 0000000..01880c9 --- /dev/null +++ b/docs/Reference/Core/Delegate.md @@ -0,0 +1,102 @@ +--- +title: Delegate +parent: Statements +permalink: /tB/Core/Delegate +--- +# Delegate +{: .no_toc } + +Declares a function-pointer type — a named signature that variables, parameters, and UDT members can hold a *reference* to a callable matching. A delegate value is bit-compatible with **LongPtr**, but adds compile-time signature checking when it is assigned, passed, or called. + +> [!NOTE] +> The **Delegate** statement is a twinBASIC extension. In classic VBA, function pointers are untyped **LongPtr** values produced by **AddressOf** and called indirectly through hand-rolled mechanisms (`DispCallFunc`, `CallWindowProc` shims, etc.). + +Syntax: +> [ **Public** \| **Private** ] **Delegate Function** *name* [ **CDecl** ] **(** [ *arglist* ] **)** **As** *type* + +**Public** +: *optional* In an ActiveX project, exports the delegate type to the type library so consumers in other projects see *name*. + +**Private** +: *optional* Withholds the delegate from the type library; usable only within the project. + +*name* +: The identifier naming the delegate type. Must be a valid twinBASIC identifier. + +**CDecl** +: *optional* Marks the delegate as using the C calling convention (`cdecl` — caller cleans the stack), used to model callbacks expected by C-runtime APIs such as `qsort`. The default is `stdcall`. See [API Declarations](../../Features/Advanced/API-Declarations#cdecl-callbacks). + +*arglist* +: *optional* Parameter signature, written exactly as for a [**Sub**](Sub) or [**Function**](Function) — comma-separated `[ ByVal | ByRef ] [ Optional ] *varname* [ As *type* ]` parts. + +*type* +: Return type of the delegate's signature. + +After the declaration, *name* may be used wherever a type is allowed: to declare variables and parameters of function-pointer type, as the type of a member of a [**Type**](Type) (UDT), or as a parameter type in a [**Declare**](Declare) statement or an [**Interface**](Interface) member. + +A delegate value is normally produced by **AddressOf**, which yields a delegate-typed reference to a regular procedure with a matching signature. For backwards compatibility, a delegate variable can also be assigned a plain **LongPtr** address obtained by other means — the value passes through unchecked. A delegate variable is called like a function: `result = myDelegate(arg1, arg2)`. + +### Example + +A basic delegate, declared, assigned, and called: + +```tb +Private Delegate Function Operation (ByVal A As Long, ByVal B As Long) As Long + +Public Function Addition(ByVal A As Long, ByVal B As Long) As Long + Return A + B +End Function + +Private Sub Command1_Click() + Dim op As Operation = AddressOf Addition + MsgBox "Answer: " & op(5, 6) +End Sub +``` + +A delegate used as a UDT member, modelling the `lpfnHook` field of the Windows `CHOOSECOLOR` struct. Existing code that assigns a **Long**/**LongPtr** to `lpfnHook` continues to work; new code can assign **AddressOf** *Handler* directly and have the signature checked at compile time: + +```tb +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 ' Typed function pointer instead of LongPtr. + lpTemplateName As LongPtr +End Type + +Dim tCC As CHOOSECOLOR +tCC.lpfnHook = AddressOf ChooseColorHookProc +``` + +A **CDecl** delegate, used as the comparator parameter of the C-runtime `qsort` API: + +```tb +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 _ +) +``` + +### See Also + +- [**Declare** statement](Declare) +- [**Type** statement](Type) +- [**Interface** statement](Interface) +- [**Alias** statement](Alias) +- [Delegate Types](../../Features/Language/Delegates) +- [API Declarations](../../Features/Advanced/API-Declarations) +- [Enhanced Pointer Functionality](../../Features/Language/Pointers) diff --git a/docs/Reference/Statements.md b/docs/Reference/Statements.md index dc4990c..b9bcaca 100644 --- a/docs/Reference/Statements.md +++ b/docs/Reference/Statements.md @@ -28,6 +28,8 @@ These statements are built into the language itself. They are understood by the * [Declare](../tB/Core/Declare) -- declares references to external procedures in a dynamic-link library (DLL) +* [Delegate](../tB/Core/Delegate) -- (twinBASIC) declares a function-pointer type — a named signature for indirect calls + * [Dim](../tB/Core/Dim) -- declares variables and allocates storage space * [Do ... Loop](../tB/Core/Do-Loop) -- repeats a block of statements while a condition is **True** or until a condition becomes **True** From c792a51f65f64db55c0158e8b9d06c248a830f50 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Mon, 11 May 2026 17:24:07 +0200 Subject: [PATCH 6/9] Document AddressOf. --- docs/Reference/Core/AddressOf.md | 96 ++++++++++++++++++++++++++++++++ docs/Reference/Operators.md | 4 ++ 2 files changed, 100 insertions(+) create mode 100644 docs/Reference/Core/AddressOf.md diff --git a/docs/Reference/Core/AddressOf.md b/docs/Reference/Core/AddressOf.md new file mode 100644 index 0000000..742eefd --- /dev/null +++ b/docs/Reference/Core/AddressOf.md @@ -0,0 +1,96 @@ +--- +title: AddressOf +parent: Operators +grand_parent: Reference Section +permalink: /tB/Core/AddressOf +--- +# AddressOf operator +{: .no_toc } + +A unary operator that returns a function-pointer reference to its operand. + +Syntax: +> **AddressOf** *procedurename* +> **AddressOf** *instance*.*procedurename* *(twinBASIC)* + +*procedurename* +: The name of a [**Sub**](Sub), [**Function**](Function), or [**Property**](Property) procedure whose address is taken. + +*instance* +: *optional* (twinBASIC) An object reference whose member *procedurename* is targeted. The resulting pointer is bound to *instance*, so calling through it invokes the method on that specific object. + +When a procedure name appears in an argument list, normally the procedure is *called* and the procedure's return value is passed. **AddressOf** suppresses the call and substitutes the procedure's address instead. The most common use is to install a callback in a Windows API — the API then invokes the procedure from outside your code, in a process known as a *callback*. + +The value **AddressOf** produces is bit-compatible with **LongPtr**, so it can be passed wherever a function pointer is expected — including legacy [**Declare**](Declare) parameters typed **As Long** or **As LongPtr**. When the destination type is a [**Delegate**](Delegate), the compiler additionally checks that the operand's signature matches the delegate's. + +In classic VBA, *procedurename* must name a procedure in a standard [**Module**](Module) of the current project; the destination parameter must be typed **As Long**; and the resulting pointer can only be invoked by code outside Basic (e.g. a DLL). twinBASIC lifts each of these restrictions — see [twinBASIC enhancements](#twinbasic-enhancements) below. + +> [!NOTE] +> Errors raised inside a callback cannot propagate back to the foreign caller — the API runs outside your project's error-handling chain. Place `On Error Resume Next` (or an explicit handler) at the top of any procedure used as an **AddressOf** target. + +### twinBASIC enhancements + +- **Indirect calls back through Basic.** A delegate variable holding an **AddressOf** value can be called directly: `Dim op As Operation = AddressOf Add: r = op(5, 6)`. Classic VBA can pass such pointers between procedures but cannot invoke through them inside Basic. See [**Delegate**](Delegate). +- **Class, form, and user-control members.** **AddressOf** accepts methods declared on a class, form, or user-control. Take a pointer to an instance method by qualifying the name with the object reference: `AddressOf myInstance.MyMethod`. The resulting pointer remembers the instance — calling through it dispatches to that object. +- **CDecl callbacks.** Mark both the target procedure and the matching [**Delegate**](Delegate) (or [**Declare**](Declare) parameter) with **CDecl** to model `cdecl` callbacks. Classic VBA's **AddressOf** is hard-wired to `__stdcall`. See [API Declarations](../../Features/Advanced/API-Declarations#cdecl-callbacks). +- **No `FARPROC` shim needed.** Assigning a function pointer to a local variable is direct — `Dim lpfn As LongPtr = AddressOf MyFunc` — without writing an intermediate forwarding procedure. + +### Example + +Calling through a typed delegate, inside Basic: + +```tb +Private Delegate Function Operation (ByVal A As Long, ByVal B As Long) As Long + +Public Function Addition(ByVal A As Long, ByVal B As Long) As Long + Return A + B +End Function + +Private Sub Demo() + Dim op As Operation = AddressOf Addition + Debug.Print op(5, 6) ' 11 +End Sub +``` + +Installing a callback in a Win32 API. `EnumWindows` invokes *EnumProc* once per top-level window: + +```tb +Public Declare PtrSafe Function EnumWindows Lib "user32" ( _ + ByVal lpEnumFunc As LongPtr, ByVal lParam As LongPtr) As Long + +Public Function EnumProc(ByVal hwnd As LongPtr, ByVal lParam As LongPtr) As Long + On Error Resume Next + Debug.Print hwnd + EnumProc = 1 ' Continue enumeration. +End Function + +Public Sub ListTopLevelWindows() + EnumWindows AddressOf EnumProc, 0 +End Sub +``` + +Taking a pointer to an instance method by qualifying with the object reference: + +```tb +Class CFoo + Public Sub Bar() + Debug.Print "Bar on instance" + End Sub +End Class + +Public Sub Demo() + Dim foo1 As New CFoo + Dim lpfn As LongPtr = AddressOf foo1.Bar +End Sub +``` + +### See Also + +- [**Delegate** statement](Delegate) +- [**Declare** statement](Declare) +- [Delegate Types](../../Features/Language/Delegates) +- [Enhanced Pointer Functionality](../../Features/Language/Pointers) +- [API Declarations](../../Features/Advanced/API-Declarations) +- [Operators](../../Reference/Operators) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Operators.md b/docs/Reference/Operators.md index dc754f4..f440257 100644 --- a/docs/Reference/Operators.md +++ b/docs/Reference/Operators.md @@ -36,3 +36,7 @@ The right operand is evaluated only when the left operand does not already deter - [Is](../tB/Core/Is) -- compares two object references for identity - [IsNot](../tB/Core/IsNot) -- (twinBASIC) the logical inverse of **Is** + +## Function Pointers + +- [AddressOf](../tB/Core/AddressOf) -- produces a typed function-pointer to a procedure From 972ae31983d471775981afd32bb5881585092e8a Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Mon, 11 May 2026 17:39:11 +0200 Subject: [PATCH 7/9] Document the Handles keyword. --- docs/Reference/Core/Function.md | 2 +- docs/Reference/Core/Handles.md | 75 +++++++++++++++++++++++++++++++++ docs/Reference/Core/Property.md | 3 +- docs/Reference/Core/Sub.md | 3 +- docs/Reference/Statements.md | 2 + 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 docs/Reference/Core/Handles.md diff --git a/docs/Reference/Core/Function.md b/docs/Reference/Core/Function.md index 4214ce5..671bfde 100644 --- a/docs/Reference/Core/Function.md +++ b/docs/Reference/Core/Function.md @@ -56,7 +56,7 @@ Syntax: *binding-clause* : *optional* (twinBASIC) One of three trailing clauses that bind this body to a member declared elsewhere: - - **Handles** *object*.*event* — wires this **Function** up as a handler for the named event, replacing the traditional `Object_Event` naming convention. See [Handler Method Syntax](../../Features/Language/Handlers). + - **Handles** *object*.*event* [ **,** *object*.*event* … ] — wires this **Function** up as a handler for the named event(s), replacing the traditional `Object_Event` naming convention. See [**Handles** statement](Handles). - **Implements** *iface*.*member* [ **,** *iface2*.*member2* … ] — provides the body for the named [**Interface**](Interface) (or [**Class**](Class)) member, replacing the traditional `Iface_Member` naming convention. A comma-separated list lets one body satisfy several interfaces' members at once. See [**Implements** statement](Implements). - **Overrides** *base*.*member* — supplies the body for an **Overridable** *member* inherited via [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop). Combine with **Overridable** on the same header to allow further-derived classes to override again. diff --git a/docs/Reference/Core/Handles.md b/docs/Reference/Core/Handles.md new file mode 100644 index 0000000..8a1d3cc --- /dev/null +++ b/docs/Reference/Core/Handles.md @@ -0,0 +1,75 @@ +--- +title: Handles +parent: Statements +permalink: /tB/Core/Handles +--- +# Handles +{: .no_toc } + +A trailing clause on a procedure header that binds the procedure as an event handler for one or more specific events. + +> [!NOTE] +> The **Handles** clause is a twinBASIC extension. Classic VBA wires up event handlers solely by name: a `Sub` called `Form_Load` automatically handles the `Load` event of `Form`. twinBASIC still supports that pattern — **Handles** decouples the procedure name from the events it handles and lets one body handle several events at once. Whether the IDE inserts the new syntax when auto-generating event prototypes is controlled by the "IDE: Use new handles/implements syntax" option. + +Syntax: +> *procedure-header* **Handles** *object*.*event* [ **,** *object*.*event* ] … + +*procedure-header* +: A complete [**Sub**](Sub), [**Function**](Function), or [**Property**](Property) header, including any access modifier, name, parameter list, and (for **Function** / **Property Get**) return type. + +*object* +: An identifier naming an event source visible in the enclosing class, form, or user-control: the host's own implicit identifier (`Form`, `UserControl`, `MyClass`), a control declared on a form (`Command1`, `Text1`, …), or a [**WithEvents**](Dim) member variable. + +*event* +: The name of an [**Event**](Event) declared on the type of *object*. + +The procedure's parameter list must match the signatures of every event it handles. When several events are listed they must all share the same signature, so one body can service them interchangeably. + +Because **Handles** decouples the procedure's name from the events it handles, you can: + +- give the procedure a descriptive name (`OnLoad`, `SyncOpacity`) instead of the compound `_` form; +- factor several related event handlers into a single body without duplicating code; and +- handle an event from a procedure whose name happens to collide with the implicit naming pattern. + +The classic naming convention is unaffected: a procedure literally named `*object*_*event*` continues to be auto-wired as a handler for that event, with or without **Handles** clauses elsewhere on the same event. + +### Example + +A descriptively named handler for a form's `Load` event: + +```tb +Private Sub OnLoad() Handles Form.Load + Debug.Print "Form is loading." +End Sub +``` + +A single body responding to several property-change events at once (adapted from the standard `CheckMark` control): + +```tb +Protected Sub SignificantChange() _ + Handles BackColor.OnPropertyLet, _ + BackStyle.OnPropertyLet, _ + Appearance.OnPropertyLet, _ + Value.OnPropertyLet + Me.WindowlessRefresh() +End Sub +``` + +For comparison, the equivalent classic-VBA naming-convention form for one of those events: + +```tb +Private Sub BackColor_OnPropertyLet() + Me.WindowlessRefresh() +End Sub +``` + +— and you would need a separate procedure body per event. + +### See Also + +- [**Sub** statement](Sub) +- [**Function** statement](Function) +- [**Property** statement](Property) +- [**Event** statement](Event) +- [**Implements** statement](Implements) +- [Handler Method Syntax](../../Features/Language/Handlers) diff --git a/docs/Reference/Core/Property.md b/docs/Reference/Core/Property.md index 298ff92..d83c957 100644 --- a/docs/Reference/Core/Property.md +++ b/docs/Reference/Core/Property.md @@ -88,7 +88,7 @@ Syntax: *binding-clause* : *optional* (twinBASIC) One of three trailing clauses that bind this accessor to a member declared elsewhere: - - **Handles** *object*.*event* — wires the property up as a handler for the named event, replacing the traditional `Object_Event` naming convention. See [Handler Method Syntax](../../Features/Language/Handlers). + - **Handles** *object*.*event* [ **,** *object*.*event* … ] — wires the property up as a handler for the named event(s), replacing the traditional `Object_Event` naming convention. See [**Handles** statement](Handles). - **Implements** *iface*.*member* [ **,** *iface2*.*member2* … ] — provides the body for the named [**Interface**](Interface) (or [**Class**](Class)) member, replacing the traditional `Iface_Member` naming convention. A comma-separated list lets one body satisfy several interfaces' members at once. See [**Implements** statement](Implements). - **Overrides** *base*.*member* — supplies the body for an **Overridable** *member* inherited via [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop). Combine with **Overridable** on the same header to allow further-derived classes to override again. @@ -191,6 +191,7 @@ End Property - [**Exit** statement](Exit) - [**Return** statement](Return) - [**Implements** statement](Implements) +- [**Handles** statement](Handles) - [**Protected** statement](Protected) - [Handler Method Syntax](../../Features/Language/Handlers) - [Inheritance](../../Features/Language/Inheritance) diff --git a/docs/Reference/Core/Sub.md b/docs/Reference/Core/Sub.md index 2dee00d..1e3473e 100644 --- a/docs/Reference/Core/Sub.md +++ b/docs/Reference/Core/Sub.md @@ -49,7 +49,7 @@ Syntax: *binding-clause* : *optional* (twinBASIC) One of three trailing clauses that bind this body to a member declared elsewhere: - - **Handles** *object*.*event* — wires this **Sub** up as a handler for the named event, replacing the traditional `Object_Event` naming convention. See [Handler Method Syntax](../../Features/Language/Handlers). + - **Handles** *object*.*event* [ **,** *object*.*event* … ] — wires this **Sub** up as a handler for the named event(s), replacing the traditional `Object_Event` naming convention. See [**Handles** statement](Handles). - **Implements** *iface*.*member* [ **,** *iface2*.*member2* … ] — provides the body for the named [**Interface**](Interface) (or [**Class**](Class)) member, replacing the traditional `Iface_Member` naming convention. A comma-separated list lets one body satisfy several interfaces' members at once. See [**Implements** statement](Implements). - **Overrides** *base*.*member* — supplies the body for an **Overridable** *member* inherited via [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop). Combine with **Overridable** on the same header to allow further-derived classes to override again. @@ -137,6 +137,7 @@ End Sub - [**Exit** statement](Exit) - [**Return** statement](Return) - [**Implements** statement](Implements) +- [**Handles** statement](Handles) - [**Protected** statement](Protected) - [Handler Method Syntax](../../Features/Language/Handlers) - [Inheritance](../../Features/Language/Inheritance) diff --git a/docs/Reference/Statements.md b/docs/Reference/Statements.md index b9bcaca..5d96e1f 100644 --- a/docs/Reference/Statements.md +++ b/docs/Reference/Statements.md @@ -58,6 +58,8 @@ These statements are built into the language itself. They are understood by the * [GoTo](../tB/Core/GoTo) -- branches unconditionally to a specified line within a procedure +* [Handles](../tB/Core/Handles) -- (twinBASIC) binds a procedure as an event handler for one or more named events + * [If ... Then ... Else](../tB/Core/If-Then-Else) -- conditionally executes a group of statements, depending on the value of an expression * [Input #](../tB/Core/Input) -- reads data from an open sequential file and assigns it to variables From d5f1bcbb69dd62c4bfeadabdab806f883af555f6 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Mon, 11 May 2026 18:24:07 +0200 Subject: [PATCH 8/9] Document that Decimal is in fact supported. --- docs/Reference/Core/Const.md | 2 +- docs/Reference/Core/Declare.md | 4 ++-- docs/Reference/Core/Deftype.md | 2 +- docs/Reference/Core/Event.md | 2 +- docs/Reference/Core/Function.md | 4 ++-- docs/Reference/Core/Private.md | 2 +- docs/Reference/Core/Property.md | 4 ++-- docs/Reference/Core/Public.md | 2 +- docs/Reference/Core/ReDim.md | 2 +- docs/Reference/Core/Static.md | 2 +- docs/Reference/Core/Sub.md | 2 +- docs/Reference/VBA/Constants/VbVarType.md | 3 --- docs/Reference/VBA/Information/VarType.md | 2 -- 13 files changed, 14 insertions(+), 19 deletions(-) diff --git a/docs/Reference/Core/Const.md b/docs/Reference/Core/Const.md index 58afa79..1e395ce 100644 --- a/docs/Reference/Core/Const.md +++ b/docs/Reference/Core/Const.md @@ -31,7 +31,7 @@ Syntax: *type* -: *optional* The data type of the constant; may be Byte, Boolean, Integer, Long, Currency, Single, Double, Decimal (not currently supported), Date, String, or Variant. Use a separate **As** *type* clause for each constant being declared. +: *optional* The data type of the constant; may be Byte, Boolean, Integer, Long, Currency, Single, Double, Decimal, Date, String, or Variant. Use a separate **As** *type* clause for each constant being declared. *expression* diff --git a/docs/Reference/Core/Declare.md b/docs/Reference/Core/Declare.md index 141a5b7..47c8ab9 100644 --- a/docs/Reference/Core/Declare.md +++ b/docs/Reference/Core/Declare.md @@ -65,7 +65,7 @@ Syntax: : *optional* List of variables representing arguments that are passed to the procedure when it is called. *type* -: *optional* Data type of the value returned by a **Function** procedure; may be Byte, Boolean, Integer, Long, LongLong, LongPtr, Currency, Single, Double, Decimal (not currently supported), Date, String (variable length only), Variant, a user-defined type (UDT), or an object type. **LongLong** is a valid declared type only on 64-bit platforms. +: *optional* Data type of the value returned by a **Function** procedure; may be Byte, Boolean, Integer, Long, LongLong, LongPtr, Currency, Single, Double, Decimal, Date, String (variable length only), Variant, a user-defined type (UDT), or an object type. **LongLong** is a valid declared type only on 64-bit platforms. ### arglist @@ -92,7 +92,7 @@ Syntax: [ **Optional** ] [ **ByVal** \| **ByRef** ] [ **ParamArray** ] *varname* : Required for array variables. Indicates that *varname* is an array. *type* -: *optional* Data type of the argument passed to the procedure; may be **Byte**, **Boolean**, **Integer**, **Long**, **LongLong**, **LongPtr**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (variable length only), **Object**, **Variant**, a user-defined type (UDT), or an object type. (**LongLong** is a valid declared type only on 64-bit platforms.) +: *optional* Data type of the argument passed to the procedure; may be **Byte**, **Boolean**, **Integer**, **Long**, **LongLong**, **LongPtr**, **Currency**, **Single**, **Double**, **Decimal**, **Date**, **String** (variable length only), **Object**, **Variant**, a user-defined type (UDT), or an object type. (**LongLong** is a valid declared type only on 64-bit platforms.) If you include an argument list, the number and type of arguments are checked each time the procedure is called. The First sub in the following example takes one **Long** argument, wherease the Second sub takes no arguments: diff --git a/docs/Reference/Core/Deftype.md b/docs/Reference/Core/Deftype.md index 6439cbf..12e1622 100644 --- a/docs/Reference/Core/Deftype.md +++ b/docs/Reference/Core/Deftype.md @@ -44,7 +44,7 @@ The statement name determines the data type: | **DefCur** | **Currency** | | **DefSng** | **Single** | | **DefDbl** | **Double** | -| **DefDec** | **Decimal** (not currently supported) | +| **DefDec** | **Decimal** | | **DefDate** | **Date** | | **DefStr** | **String** | | **DefObj** | **Object** | diff --git a/docs/Reference/Core/Event.md b/docs/Reference/Core/Event.md index c6a2e3a..c6c85a4 100644 --- a/docs/Reference/Core/Event.md +++ b/docs/Reference/Core/Event.md @@ -27,7 +27,7 @@ Syntax: [ **Public** ] **Event** *procedurename* [ (*arglist*) ] : Name of the variable representing the argument being passed to the procedure; follows standard variable naming conventions. *type* - : *optional* Data type of the argument passed to the procedure; may be Byte, Boolean, Integer, Long, Currency, Single, Double, Decimal (not currently supported), Date, String (variable length only), Object, Variant, a user-defined type (UDT), or an object type. + : *optional* Data type of the argument passed to the procedure; may be Byte, Boolean, Integer, Long, Currency, Single, Double, Decimal, Date, String (variable length only), Object, Variant, a user-defined type (UDT), or an object type. After the event has been declared, use the [**RaiseEvent**](RaiseEvent) statement to fire the event. A syntax error occurs if an **Event** declaration appears in a standard module. An event can't be declared to return a value. A typical event might be declared and raised as shown in the following fragments. diff --git a/docs/Reference/Core/Function.md b/docs/Reference/Core/Function.md index 671bfde..fa36a04 100644 --- a/docs/Reference/Core/Function.md +++ b/docs/Reference/Core/Function.md @@ -51,7 +51,7 @@ Syntax: : *optional* List of variables representing arguments that are passed to the **Function** procedure when it is called. Multiple variables are separated by commas. **As** *type* -: *optional* Data type of the value returned by the **Function** procedure; may be Byte, Boolean, Integer, Long, Currency, Single, Double, Decimal (not currently supported), Date, String (except fixed length), Object, Variant, or any user-defined type (UDT). +: *optional* Data type of the value returned by the **Function** procedure; may be Byte, Boolean, Integer, Long, Currency, Single, Double, Decimal, Date, String (except fixed length), Object, Variant, or any user-defined type (UDT). *binding-clause* : *optional* (twinBASIC) One of three trailing clauses that bind this body to a member declared elsewhere: @@ -99,7 +99,7 @@ Syntax: One or more of : Name of the variable representing the argument; follows standard variable naming conventions. *type* -: *optional* Data type of the argument passed to the procedure; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported) **Date**, **String** (variable length only), **Object**, **Variant**, a specific object type, or the name of a generic type argument. If the parameter is not **Optional**, a user-defined type may also be specified. +: *optional* Data type of the argument passed to the procedure; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal**, **Date**, **String** (variable length only), **Object**, **Variant**, a specific object type, or the name of a generic type argument. If the parameter is not **Optional**, a user-defined type may also be specified. If the name of a generic type parameter is used, it becomes bound to the concrete type of the argument passed to the function. The name binding has the scope of the body of the function. *defaultvalue* diff --git a/docs/Reference/Core/Private.md b/docs/Reference/Core/Private.md index 75ad711..3e0a1e0 100644 --- a/docs/Reference/Core/Private.md +++ b/docs/Reference/Core/Private.md @@ -24,7 +24,7 @@ Syntax: : *optional* Keyword that enables implicit creation of an object. If you use **New** when declaring the object variable, a new instance of the object is created on first reference to it, so you don't have to use the **[Set](Set)** statement to assign the object reference. The **New** keyword can't be used to declare variables of any intrinsic data type or to declare instances of dependent objects, and it can't be used with **WithEvents**. *type* -: *optional* Data type of the variable; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, a user-defined type, or an object type. Use a separate **As** *type* clause for each variable being defined. +: *optional* Data type of the variable; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal**, **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, a user-defined type, or an object type. Use a separate **As** *type* clause for each variable being defined. **Private** variables are available only to the module in which they are declared. diff --git a/docs/Reference/Core/Property.md b/docs/Reference/Core/Property.md index d83c957..6c61176 100644 --- a/docs/Reference/Core/Property.md +++ b/docs/Reference/Core/Property.md @@ -71,7 +71,7 @@ Syntax: : List of variables representing arguments that are passed to the **Property** procedure when it is called. Multiple arguments are separated by commas. The name and data type of each argument in a **Property Let** or **Property Set** procedure must be the same as the corresponding argument in the matching **Property Get** procedure. See [*arglist*](#arglist) below for syntax. *arglist* is optional for **Property Get**; for **Property Let** and **Property Set** at least the *value*/*reference* parameter is required. **As** *type* -: *optional* Data type of the value returned by the **Property Get** procedure; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (except fixed length), **Object**, **Variant**, a user-defined type, or an array. The return *type* of a **Property Get** procedure must be the same data type as the *value* parameter of the corresponding **Property Let** procedure (if one exists), or compatible with the *reference* parameter of the corresponding **Property Set** procedure. +: *optional* Data type of the value returned by the **Property Get** procedure; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal**, **Date**, **String** (except fixed length), **Object**, **Variant**, a user-defined type, or an array. The return *type* of a **Property Get** procedure must be the same data type as the *value* parameter of the corresponding **Property Let** procedure (if one exists), or compatible with the *reference* parameter of the corresponding **Property Set** procedure. *statements* : *optional* Any group of statements to be executed within the body of the **Property** procedure. @@ -119,7 +119,7 @@ Syntax: One or more of : Name of the variable representing the argument; follows standard variable naming conventions. *type* -: *optional* Data type of the argument passed to the procedure; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (variable length only), **Object**, **Variant**, a specific object type, or the name of a generic type argument. If the parameter is not **Optional**, a user-defined type may also be specified. +: *optional* Data type of the argument passed to the procedure; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal**, **Date**, **String** (variable length only), **Object**, **Variant**, a specific object type, or the name of a generic type argument. If the parameter is not **Optional**, a user-defined type may also be specified. If the name of a generic type parameter is used, it becomes bound to the concrete type of the argument passed to the procedure. The name binding has the scope of the body of the procedure. *defaultvalue* diff --git a/docs/Reference/Core/Public.md b/docs/Reference/Core/Public.md index 28fe5c4..b209bd7 100644 --- a/docs/Reference/Core/Public.md +++ b/docs/Reference/Core/Public.md @@ -24,7 +24,7 @@ Syntax: : *optional* Keyword that enables implicit creation of an object. If you use **New** when declaring the object variable, a new instance of the object is created on first reference to it, so you don't have to use the **[Set](Set)** statement to assign the object reference. The **New** keyword can't be used to declare variables of any intrinsic data type or to declare instances of dependent objects, and it can't be used with **WithEvents**. *type* -: *optional* Data type of the variable; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, a user-defined type, or an object type. Use a separate **As** *type* clause for each variable being defined. +: *optional* Data type of the variable; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal**, **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, a user-defined type, or an object type. Use a separate **As** *type* clause for each variable being defined. Variables declared by using the **Public** statement are available to all procedures in all modules in all applications, unless **[Option Private Module](Option)** is in effect; in which case, the variables are public only within the project in which they reside. diff --git a/docs/Reference/Core/ReDim.md b/docs/Reference/Core/ReDim.md index a563d91..ad2c471 100644 --- a/docs/Reference/Core/ReDim.md +++ b/docs/Reference/Core/ReDim.md @@ -21,7 +21,7 @@ Syntax: : Dimensions of an array variable; up to 60 multiple dimensions may be declared. The *subscripts* argument uses the following syntax: [ *lower* **To** ] *upper* [ , [ *lower* **To** ] *upper* ] **. . .**. When not explicitly stated in *lower*, the lower bound of an array is controlled by the [**Option Base**](Option#Base) statement. The lower bound is zero if no **Option Base** statement is present. *type* -: *optional* Data type of the variable; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, a user-defined type, or an object type. +: *optional* Data type of the variable; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal**, **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, a user-defined type, or an object type. Use a separate **As** *type* clause for each variable being defined. For a **Variant** containing an array, *type* describes the type of each element of the array, but doesn't change the **Variant** to some other type. The **ReDim** statement is used to size or resize a dynamic array that has already been formally declared by using a [**Private**](Private), [**Public**](Public), or [**Dim**](Dim) statement with empty parentheses (without dimension subscripts). diff --git a/docs/Reference/Core/Static.md b/docs/Reference/Core/Static.md index 799a7ba..d74f151 100644 --- a/docs/Reference/Core/Static.md +++ b/docs/Reference/Core/Static.md @@ -21,7 +21,7 @@ Syntax: : *optional* Keyword that enables implicit creation of an object. If you use **New** when declaring the object variable, a new instance of the object is created on first reference to it, so you don't have to use the **[Set](Set)** statement to assign the object reference. The **New** keyword can't be used to declare variables of any intrinsic data type or to declare instances of dependent objects. *type* -: *optional* Data type of the variable; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, a user-defined type, or an object type. Use a separate **As** *type* clause for each variable being defined. +: *optional* Data type of the variable; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal**, **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, a user-defined type, or an object type. Use a separate **As** *type* clause for each variable being defined. After module code is running, variables declared with the **Static** statement retain their value until the module is reset or restarted. In class modules, variables declared with the **Static** statement retain their value in each class instance until that instance is destroyed. In form modules, static variables retain their value until the form is closed. diff --git a/docs/Reference/Core/Sub.md b/docs/Reference/Core/Sub.md index 1e3473e..3793c2e 100644 --- a/docs/Reference/Core/Sub.md +++ b/docs/Reference/Core/Sub.md @@ -80,7 +80,7 @@ Syntax: One or more of : Name of the variable representing the argument; follows standard variable naming conventions. *type* -: *optional* Data type of the argument passed to the procedure; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (variable length only), **Object**, **Variant**, a specific object type, or the name of a generic type argument. If the parameter is not **Optional**, a user-defined type may also be specified. +: *optional* Data type of the argument passed to the procedure; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal**, **Date**, **String** (variable length only), **Object**, **Variant**, a specific object type, or the name of a generic type argument. If the parameter is not **Optional**, a user-defined type may also be specified. If the name of a generic type parameter is used, it becomes bound to the concrete type of the argument passed to the procedure. The name binding has the scope of the body of the procedure. *defaultvalue* diff --git a/docs/Reference/VBA/Constants/VbVarType.md b/docs/Reference/VBA/Constants/VbVarType.md index 98701bd..737849e 100644 --- a/docs/Reference/VBA/Constants/VbVarType.md +++ b/docs/Reference/VBA/Constants/VbVarType.md @@ -51,7 +51,4 @@ Variant subtype codes returned by the **VarType** function. Most calls return a | **vbUserDefinedType**{: #vbUserDefinedType } | 36 | **Variant** containing a user-defined type. | | **vbArray**{: #vbArray } | 8192 | Array. Always added to another value when returned. | -> [!NOTE] -> The **Decimal** subtype (**vbDecimal**) is reserved but not currently supported in twinBASIC. The constant is defined for source compatibility with VBA. - {% include VBA-Attribution.md %} diff --git a/docs/Reference/VBA/Information/VarType.md b/docs/Reference/VBA/Information/VarType.md index e52e2bc..1cf8043 100644 --- a/docs/Reference/VBA/Information/VarType.md +++ b/docs/Reference/VBA/Information/VarType.md @@ -44,8 +44,6 @@ If an object is passed and has a default property, **VarType(*object*)** returns > [!NOTE] > twinBASIC also exposes a generic form, **VarType(Of *T*)**, which is useful for compile-time verification of generic type specifiers. The non-generic call uses special internal bindings and so may not behave like a regular function. -> -> The **Decimal** subtype (`vbDecimal`) is reserved but not currently supported in twinBASIC; the constant is defined for source compatibility with VBA. ### Example From a7b204a68abd8b45225f018b1c283b79b76b6c6f Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Mon, 11 May 2026 18:30:19 +0200 Subject: [PATCH 9/9] Fix formatting in Declare. --- docs/Reference/Core/Declare.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/Reference/Core/Declare.md b/docs/Reference/Core/Declare.md index 47c8ab9..fbaaf7c 100644 --- a/docs/Reference/Core/Declare.md +++ b/docs/Reference/Core/Declare.md @@ -10,7 +10,9 @@ Used at the module level to declare references to external procedures in a dynam > [!NOTE] > -> **Declare** statements with the PtrSafe keyword is the recommended syntax. **Declare** statements that include **PtrSafe** work correctly in twinBASIC and VBA version 7 development environment on both 32-bit and 64-bit platforms only after all data types in the **Declare** statement (parameters and return values) that need to store 64-bit quantities are updated to use LongLong for 64-bit integrals or LongPtr for pointers and handles. To ensure backwards compatibility with VBA version 6 and earlier, use the following construct: +> **Declare** statements with the PtrSafe keyword is the recommended syntax. **Declare** statements that include **PtrSafe** work correctly in twinBASIC and VBA version 7 development environment on both 32-bit and 64-bit platforms only after all data types in the **Declare** statement (parameters and return values) that need to store 64-bit quantities are updated to use LongLong for 64-bit integrals or LongPtr for pointers and handles. + +To ensure backwards compatibility with VBA version 6 and earlier, use the following construct: ```tb #If VBA7 Then @@ -19,6 +21,7 @@ Declare PtrSafe Sub... Declare Sub... #EndIf ``` + > [!NOTE] > > For code to run when built for 64-bit targets, all **Declare** statements must include the **PtrSafe** keyword, and all data types in the **Declare** statement (parameters and return values) that need to store 64-bit quantities must be updated to use **LongLong** for 64-bit integrals or **LongPtr** for pointers and handles.