twinBASIC Update: November 21, 2025
On April 23, 2021, I helped Wayne Phillips introduce the world to twinBASIC at the Access DevCon Vienna conference. I boldly predicted that twinBASIC (along with the Monaco editor) would replace VBA and its outdated development environment by 2025. With that goal in mind, this weekly update is my attempt to keep the project fresh in the minds of the VBA development community.
Every Sunday Monday week, I will be providing updates on the status of the project, linking to new articles discussing twinBASIC, and generally trying to increase engagement with the project. If you come across items that should be included here, please leave a comment below.
Here are some links to get involved with the project:
- Custom twinBASIC IDE Installation Guide
- twinBASIC Discord Server (chat about the project)
- twinBASIC Wiki (list of new features not in VBx)
- GitHub Issue Tracker (report bugs)
- twinBASIC/VBx LinkedIn Group
Highlights
*Written with the help of Claude-Sonnet-4.5
Enhanced UDTs: Lightweight Objects Without the Overhead
BETA 896 introduced a transformative enhancement to User-Defined Types, bringing them closer to C-style structs with methods and properties. UDTs now support Type_Initialize, Type_Terminate, Type_Assignment, Type_Conversion, and Type_DebugView procedures, enabling object-like behavior while remaining stack-allocated value types (combining the power of classes with the efficiency of value types like String or Long).
Wayne demonstrated the power of this feature with a UTF-8 wrapper type that simplifies API calls:
Dim str As UTF8 = "twinBASIC rocks!"
MessageBoxA(vbNullPtr, str, title, 0) ' str automatically converts to char*The built-in packages now leverage UDT wrappers for Win32 handles, enabling intuitive member-style syntax. This approach significantly improves discoverability via IntelliSense:
myHwnd.ShowWindow(SW_SHOWNORMAL) 'Instead of ShowWindow(myHwnd, SW_SHOWNORMAL)
myHwnd.BringWindowToTop() 'IntelliSense reveals available methodsCommunity members quickly adopted these features, with XelNiya creating a working HSTRING wrapper demonstrating lifecycle management and COM interop patterns. The feature supports overloaded Type_Assignment procedures, allowing a single UDT to accept multiple source types seamlessly.
ByVal UDT Parameters: Proper Value Semantics
Separate from the UDT methods and properties enhancement, BETA 896 also introduced support for passing simple UDTs ByVal to procedures and API calls. This addresses the calling convention (how UDTs are physically passed between functions) rather than what UDTs can do internally.
Simple UDTs (those without String, Object, Variant, or dynamic array fields) can now be passed ByVal. The compiler automatically handles the calling convention details, converting to ByRef when structures exceed 8 bytes in 64-bit builds.
This change brings twinBASIC in line with how structures work in C, C++, and .NET, while maintaining VB6 compatibility for existing ByRef usage. The two UDT enhancements work together powerfully - for example, Wayne's UTF8 type uses Type_Conversion (from Enhanced UDTs) to convert to a char pointer, while ByVal support allows passing it directly to APIs without the awkward ByRef syntax.
fafalone updated WinDevLib with ByVal UDT support across 123+ version-gated declarations, demonstrating the widespread applicability of this enhancement.
Memory Leak Fixes: Form Designer Finally Clean
After extensive investigation, Wayne resolved the major memory leaks that had plagued the form designer. This long-standing issue affected projects with heavy form usage, and its resolution marks a significant stability milestone for the platform.
Executable Size Reductions: 20-25% Smaller Binaries
Architectural improvements in BETA 896 yielded substantial executable size reductions across the board:
- PhotoDemon: 26MB → 21MB
- Lemmings (32-bit): 2.2MB → 1.7MB
- Lemmings (64-bit): 3.1MB → 2.5MB
Lemmings is a UI-intensive VB6 game port that Wayne frequently uses for regression testing following major refactorings.
These reductions weren't a primary goal but emerged as a beneficial side effect of the form engine optimizations and control inheritance improvements.
Discord Chat Summary
* Auto-generated via Claude-Sonnet-4.5
Overview
This week saw a major architectural milestone with BETA 896's release, introducing enhanced UDT (User-Defined Type) capabilities that fundamentally expand twinBASIC's type system. The community actively explored these new features, particularly UDT methods, properties, and lifecycle management, while Wayne continued aggressive optimization work that reduced executable sizes by 20-25% across major projects. Discussion centered on the practical implications of these changes, with extensive testing revealing both the power and complexity of the new UDT system.
Language Enhancements
- UDT methods and properties: UDTs now support
Type_Initialize,Type_Terminate,Type_Assignment,Type_Conversion, andType_DebugViewprocedures, enabling lightweight object-like behavior without reference counting overhead - ByVal UDT parameters: Simple UDTs (no String/Object/Variant/dynamic array fields) can now be passed ByVal, with automatic conversion to ByRef for structures >8 bytes in 64-bit
- Wayne demonstrated UTF8 type wrapper allowing direct passing to char* APIs:
MessageBoxA(vbNullPtr, str, title, 0)where str is a UTF8 UDT - Built-in packages now use UDT wrappers for Win32 handles (HWND, HDC), enabling cleaner syntax like
myHwnd.ShowWindow(SW_SHOWNORMAL) - Type_Assignment supports overloading for multiple source types, enabling flexible conversion patterns
Bug Fixes & Stability
- Major memory leak fixes: Form designer leaks resolved in BETA 896
- Type_Terminate execution inconsistencies discovered across 32-bit/64-bit and after compiler restarts, particularly affecting UDTs in packages
- Save dialog error continues to elude reproduction despite extensive testing, appears related to undo/redo timing and async save operations
- Intellisense micro-stutters eliminated, significantly improving IDE responsiveness when editing large packages
- Print statement form invalidation bug identified where forms erase on focus loss
Community Exploration
- xelniya created working HSTRING wrapper demonstrating UDT lifecycle management, conversion operators, and COM interop patterns
- VanGoghGaming showcased Google OAuth2 project using WinRT classes including SignalNotifier for event-driven programming without DoEvents
- fafalone updated WinDevLib throughout with ByVal UDT support, affecting 123+ version-gated declarations
- Discussion of async/await patterns, with consensus that while implementation is complex, the feature would be transformative for twinBASIC
Performance & Optimization
- Executable size reductions: PhotoDemon 26MB→21MB, LEMS 32-bit 2.2MB→1.7MB, LEMS 64-bit 3.1MB→2.5MB
- New project feature flags allow disabling unused functionality (OLE Drag Drop saves ~100KB, up to 300KB total in larger projects)
- 10Tec released 64-bit ImageList ActiveX control written in twinBASIC, demonstrating the platform's maturity for commercial component development
Development Workflow
- twixydon successfully migrated XYplorer from BETA 895 to 903, reporting smooth transition despite extensive behind-the-scenes changes
- Keyboard shortcut customization discussed for intellisense behavior, with community members sharing configurations to match personal preferences
- Monaco editor fold commands available but need better organization/discoverability for common use cases
Conclusion
BETA 896 represents a watershed moment for twinBASIC's type system, bringing struct-like capabilities that blur the line between value types and objects. The community's rapid adoption and creative exploration of UDT features—from HSTRING wrappers to Win32 handle abstractions—demonstrates both the power and maturity of these additions. While some edge cases in Type_Terminate execution need resolution, the architectural foundation is solid. Combined with continued performance improvements and the growing ecosystem of twinBASIC-native components, the platform is increasingly attractive not just for VB6 migration but as a modern development choice in its own right.
Around the Web
WinDevLib v9.2 with ByVal UDTs is now out!
Posted by fafalone in the WinDevLib Updates thread in show-and-tell:
(Hopefully) all UDTs that should be ByVal now are... no more separate 32/64bit calls, no more LongLong<->UDT pain... this is a major breaking change but going forward will be much nicer.
Update (v9.2.622, 18 Nov 2025):
- MAJOR BREAKING CHANGES twinBASIC now supports ByVal UDTs and WinDevLib will now also use these in all applicable situations. The initial conversion has been completed for all files. All types >8 bytes were easy to identify as these had separate 32/64bit defs; 8 byte types should be complete as all uses of LongLong were examined. However for less than 8 bytes, it's possible some were missed if they weren't tagged, so reviewing for these will be ongoing and lengthy. Please notify me of any UDTs that should now be ByVal that were missed.
These changes are version gated, so are only active in tB Beta 896 and newer and the old definitions are still active in Beta 895 and earlier.
Please report any crashes or related bugs. - Added custom helper WCHARtoSTR. Converts an Integer arrat of WCHARs to a tB String (BSTR).
- Misc additions
- (Bug fix) IApplicationDesignModeSettings::IsApplicationViewStateSupported last argument should be ByRef.
- (Bug fix) ID3D12VideoEncoder::GetCodecProfile,GetCodecConfiguration defs incorrect
- (Bug fix) ID2D1Transform::MapInvalidRect definition incorrect for 32bit
- (Bug fix) GdipWarpPath definition incorrect for 32bit
Send Emails with Google OAuth2 and WinRT Classes
Posted by VanGoghGaming in show-and-tell:
This twinBASIC project demonstrates the process of sending emails using the Google OAuth2 protocol and PKCE (Proof Key for Code Exchange) for enhanced security. The following WinRT classes are showcased, in no particular order:
- HttpClient, HttpResponseMessage: for posting HTTPS requests and parsing the response
- StreamSocketListener: for retrieving the authorization code from your browser after a successful authorization request
- CryptographicBuffer, HashAlgorithmProvider: for generating the PKCE challenge as well as Base64 encoding
- PasswordVault, PasswordCredential: for saving the RefreshToken in the Credential Manager (a RefreshToken can be used to retrieve a new AccessToken without going through the whole browser authorization)
- JsonObject, JsonValue: for assembling and parsing Json responses
- ThreadPoolTimer: for implementing a timeout during the browser authorization phase
- Launcher, LauncherOptions: for launching an URI in a browser of your choice
- FileOpenPicker: for selecting attachment files
- StorageFile, StorageItem: for processing attachments
Download it from this vbForums thread:
Convert CLS to TWIN
Posted by fafalone in show-and-tell:
As a temporary measure I've created a rudimentary utility to mass convert .cls files to .twin.
It will optionally add new ClassId, InterfaceId, and EventInterfaceId attributes. Subdir structure should be preserved or optionally flattened (all saved in same folder, easier to import).
This is an external utility, not an addin, so after converting use the Add -> Import option to bring the new .twin files into your project.
I did basic testing but that's it, so let me know any problems. Time permitting I may add ability to read existing GUIDs from an ocx/dll in the future, but that's a much longer task.
Changelog
Here are the updates from the past week. You can also find this information by visiting the GitHub twinBASIC Releases page.
AI-Generated Changelog Summary
* Auto-generated via Claude-Sonnet-4.5, sorted in order of its opinion of "most impactful changes."
-
Feature Flags System: Added experimental "Feature Flags" in project settings to explicitly disable unused features, reducing compilation complexity and executable size. The previous "Include Runtime PNG support" setting has been migrated to this new system.
-
Rewritten Reindent Functionality: Complete rewrite of the code reindent feature (contributed by Nikos Kesoglidis), now supporting Interfaces and providing more comprehensive formatting capabilities.
-
UDT Procedure Members & Enhanced OOP: Added support for declaring procedure members within User Defined Types (UDTs) and improved handling of ByVal passing for non-complex UDTs. Also enhanced Protected member access in object-oriented programming scenarios.
-
Forms Engine Refactoring: Massive internal refactoring of the forms engine to utilize modern twinBASIC features like Inheritance and UDT procedure members, resulting in smaller generated code and improved runtime efficiency. Multiple GDI and memory leaks were fixed throughout the forms system.
-
Per-Monitor DPI Improvements: Added
DPIChangeevent for Form, MDIForm, PropertyPage, UserControl, and Report objects when applications are per-monitor DPI aware, along with numerous fixes for DPI-related runtime issues. -
UserControl Custom Verbs: Added support for custom verbs in ActiveX UserControls through the
UserControl.Verbsstring array and correspondingVerbInvokedevent, enabling enhanced context menu functionality in AX hosts. -
Breaking Change: Passing a LongPtr to a ByRef-UDT parameter now requires the
ByValinline attribute for clarity and consistency. See this discussion for more details.
WARNING: The following issues are present in each of the releases below:
- IMPORTANT: This is an interim/experimental release. It includes significant changes, so some instability is to be expected.
- NOTE: CustomControls are currently broken in this release (e.g. Samples 6 and 8)
- NOTE: Some regressions are expected due to the significant architectural changes in this release
BETA 896
- improved: Reindent functionality has beeen re-written and donated by Nikos Kesoglidis (sokinkeso), now supporting Interfaces and more
- improved: some codegen improvements, leading to slightly smaller built executables
- added: "Feature Flags" in project settings, for explicitly turning off unused features to reduce compilation complexity and generated code size (experimental)
- removed: project setting "Include Runtime PNG support", replaced with a feature flag instead
- fixed: "X As Y" expressions now validate both sides are an object/interface type (or Variant) at compile time rather than raising a codegen error
- improved: many internal efficiency and codegen improvements around the main forms engine and the C++ interop
- improved: OOP Protected member access
- fixed: stepping into a procedure with Trace mode on in project settings, could cause a crash when exiting the procedure
- fixed: IDE hover tooltips sometimes restricted to 400px width, with further text overflowing outside the hover box
- fixed: double clicking a file in the PROJECT EXPLORER panel would activate the editor but not focus it
- fixed: context menu would not appear in the PROJECT EXPLORER panel if click was in the empty space below the nodes
- fixed: IDE hover tooltips would sometimes get stuck at the top-left position of the IDE window
- added: support for ByVal passing of non-complex UDTs [ fafalone, discord ]
- added: support for procedure members to be declared within UDTs
- fixed: method overload resolution disambiguation could fail on lossy-widening conversions
- fixed: hovering over elements in the DIAGNOSTICS panel sometimes revealed a truncated tooltip
- fixed: double-clicking on whitespace areas of elements in the DIAGNOSTICS panel was sometimes not detected
- fixed: double-clicking on elements in the DIAGNOSTICS panel would not focus the editor at the revealed point
- added: warning TB0030 'Declare Alias is not required as it is identical to the declared name' (default HINT state)
- fixed: intellisense rendering stutters which were causing the IDE to lock up momentarily when displaying intellisense results
- improved: accessibility improvements for the TOOLBOX panel [ Janusz Chmiel, private ]
- improved: reduced visual flicker in the DIAGNOSTICS panel during live sync with code edits
- fixed: 1 runtime GDI pen leak in the internal graphics class used by Form/UC/PictureBox/Report/PropertyPage
- fixed: 1 runtime HMENU leak in MDIForm implementation
- fixed: 2 runtime leaks in ImageList control
- fixed: 1 runtime leak in Data control
- fixed: lots of heap memory leaks in the form designer
- fixed: several runtime leaks in the Eval() expression service used in Reports
- improved: massive refactoring of the form engine to make use of new tB features, e.g. Inheritance and UDT proc members, allowing for smaller codegen and efficiency improvements
- improved: after selecting a control using the control selector in the form designer property sheet, the EDITOR panel is now focused
- improved: double clicking a control in the toolbox will now set focus on the EDITOR panel
- improved: warnings in packages should not bleed through to the container project [ jpbro, discord ]
- fixed: 'Inherits' syntax with a circular reference would cause a compiler crash
- fixed: some edge cases in the StandardPicture property page
- improved: the 'Library Symbol' (namespace) can now be edited for referenced packages (not only COM references as in previous versions)
- fixed: some issues regarding exposing Generic procedures in packages [ VanGoghGaming, discord ]
- fixed: setting Label.Enabled property at runtime did not change its visual state until Label.Refresh() has been called [ zhou, discord ]
- fixed: Label control font not updating to reflect DPI changes in per-monitor DPI mode [ Don, discord ]
- fixed: form designer inline mini toolbar appears incorrectly above modal dialogs in the z-order
- fixed: pressing the ALT key at any time inside the IDE would later cause clicks inside the form designer to select all container controls [ Tecman, discord ]
- fixed: some DPI change at runtime issues in forms engine [ Don, discord ]
- updated: Licence Agreement (TWINBASIC LTD)
- fixed: (regression) buildAndExit32/buildAndExit64 IDE command line arguments were broken in recent builds [ fafalone, discord ]
- BREAKING CHANGE: passing a LongPtr to a ByRef-UDT parameter now requires 'ByVal' inline attribute to make the special behaviour more clear and consistent given the new ByVal offerings for UDTs
BETA 897
- fixed: (regression in 896) AX controls that act like buttons show errors for CancelChanged and DefaultChanged implementations [ caponiidae, discord ]
- fixed: (regression in 896) internal buffer overflow when loading a project in some instances [ caponiidae, discord ]
BETA 898
- fixed: removed the spurious "111" message box that can occur when moving some AX controls
BETA 899
- fixed: form designer won't open (stuck at 20% endless loop) for some complex forms with UserControls
- fixed: a newly placed tB-generated AX control onto an MS ACCESS form could cause Access some problems when trying to resize it until the form is saved and re-opened
BETA 900
- fixed: runtime GDI handle leak when resizing a control with AutoRedraw=True [ fafalone, discord ]
- fixed: runtime GDI handle leak in handling of WM_CTLCOLORSTATIC for manually created controls on a tB container [ fafalone, discord ]
BETA 901
- fixed: (regression since BETA 896) Inherits WithEvents edge case, causing internal StdFont.FontChanged event not to fire [ franic, discord ]
- fixed: (regression since BETA 896) using any of the replacement common controls would cause a runtime crash in built executables [ zhou, discord ]
- fixed: runtime DPI change could cause Label controls to crash [ Don, discord ]
BETA 902
- fixed: support for mouse capture on lightweight controls [ Don, discord ]
BETA 903
- fixed: (regression since BETA 896) loading a frame control array element at runtime was causing a freeze [ kimpos, discord ]
- added: DPIChange(ByVal NewDPI As Long) event for Form, MDIForm, PropertyPage, UserControl and Report objects, for when application is per-monitor DPI aware [ Don, discord ]
- fixed: tooltips could appear AFTER a form or underlying control has been destroyed [ Don, discord ]
BETA 904
- fixed: some stubborn ActiveX controls will crash when passed an in-memory DC to IViewObject::Draw() at design time [ JA, private ]
- fixed: some design-time resize issues with controls that like to resize themselves
- improved: some handling around WM_DPICHANGED [ Don, discord ]
BETA 905
- added: UserControl.Verbs string array (in property sheet), allowing to add custom verbs to AX host context menus [ Tecman, discord ]
- added: 'Properties' implicit UserControl verb for when property pages are attached to a UserControl [ Tecman, discord ]
- added: UserControl.VerbInvoked event for reacting to custom verbs set in the UserControl.Verbs collection [ Tecman, discord ]