twinBASIC Update: June 10, 2025

Highlights include a new [ComExport] attribute, a new "TWINBASIC_BUILD_TYPE" compiler constant, and the continued expansion of the WinDevLib project.

twinBASIC Update: June 10, 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:


Highlights

[ComExport] Attribute Added

You can now transform standard API declarations into fully reusable COM components with a single line of code.

The new [ComExport] attribute allows you to publish API Declare statements and Const fields from a standard module directly into your ActiveX project's type library. Simply place the attribute before a Public Declare or Public Const line in any standard module within your ActiveX DLL or Control project. When compiled, these declarations become visible to any COM-aware client, such as VBA or VB6, allowing them to use your functions and constants just by adding a reference to your DLL. This eliminates the need for redundant Declare statements in consumer projects, centralizing your API logic into a single, maintainable library.

This feature enables the creation of powerful, self-documenting API utility libraries that seamlessly integrate with the entire COM ecosystem.

Full Technical Recap

The [ComExport] attribute was introduced in BETA 790 as a direct response to community requests. Its purpose is to allow developers to create ActiveX DLLs that export Windows API functions and their associated constants, making them directly accessible to COM clients without requiring the client to have its own corresponding Declare statements.

How To Use It: A Practical Example
  1. Create a twinBASIC ActiveX DLL Project. Let's call it ApiHelpers.

  2. Add Sample Code to a Standard Module. Your MainModule.twin file should look like this:

    Module MainModule
    
        ' This project type is set to 'ActiveX DLL' in the Settings file
        [ComExport]
        Public Const WM_SETTEXT = &HC
    
        [ComExport]
        Public Declare PtrSafe Function FindWindowW Lib "user32" _
            (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
    
        [ComExport]
        Public Declare PtrSafe Function SendMessageW Lib "user32" _
            (ByVal hWnd As LongPtr, ByVal Msg As Long, _
             ByVal wParam As LongPtr, ByVal lParam As String) As LongPtr
    
    End Module
    
  3. Build the Project. File > Build. This will create and register ApiHelpers.dll.

  4. Use it in a COM Client (e.g., Excel VBA):

    • Open the VBA editor, go to Tools -> References, and add a reference to "ApiHelpers".
    • You can now write the following code without any local Declare statements:
      Sub SetNotepadTitle()
          Dim hWnd As LongPtr
          hWnd = ApiHelpers.FindWindowW("Notepad", vbNullString)
          
          If hWnd <> 0 Then
              ApiHelpers.SendMessageW hWnd, ApiHelpers.WM_SETTEXT, 0, "Hello from VBA!"
          End If
      End Sub
      
  5. Run the test code: Ensure Notepad is already running, then run the SetNotepadTitle() procedure above. The Notepad window's title will be changed to "Hello from VBA!"

Important Technical Considerations

  • Project Scope: The [ComExport] attribute is only effective in projects compiled as an ActiveX component (e.g., ActiveX DLL, ActiveX Control) and only applies to Public declarations within a standard module.

  • DLL Path Resolution: The Lib string in your Declare statement is critical. For a client like VBA to find the target DLL (e.g., user32.dll), it must be in a standard system search path. If you are exporting an internal function from your own DLL, be aware that the client's current working directory may not be where your DLL is located. The most reliable method is to ensure any custom DLLs are placed in a location on the system PATH.

  • Aliases and Ordinals: The feature correctly handles aliases. The name used in the Declare statement will be the name exposed in the type library, while the Alias (whether a string or an ordinal number) will be used as the actual function entry point.

  • Automatic UDT Export: If an exported Declare statement uses a User-Defined Type (UDT) as a parameter, twinBASIC will automatically export the UDT's definition to the type library as well.

  • Sub vs. Function Declarations: An initial bug caused Declare Sub statements to be exported incorrectly. This was fixed in BETA 801. Ensure you are using this version or newer to avoid issues with functions that have a void return type.

*This section was generated via Gemini-2.5-Pro based on this twinBASIC Discord channel discussion.


Fine-Tune Builds with a New Compiler Constant

You can now write a single codebase that automatically tailors itself for different compilation targets, such as a Standard EXE or an ActiveX Control.

Introduced in BETA 799, the new TWINBASIC_BUILD_TYPE compiler constant directly addresses a key challenge in creating components for both modern and legacy environments. As detailed in a GitHub issue by user fafalone, an ActiveX control using the modern LongLong data type would cause automation errors in VB6, which does not support it in type libraries. The previous workaround of using #If Win64 to switch to the compatible Currency type was too broad, as it unnecessarily penalized 32-bit twinBASIC builds that can handle LongLong perfectly. With this new constant, developers can now use #If TWINBASIC_BUILD_TYPE = "ActiveX Control" to isolate compatibility code, ensuring that only the builds intended for legacy hosts receive the necessary changes.

The following values are supported:

#If TWINBASIC_BUILD_TYPE = "Standard EXE" Then ...
#If TWINBASIC_BUILD_TYPE = "Standard DLL" Then ... 
#If TWINBASIC_BUILD_TYPE = "ActiveX DLL" Then ...
#If TWINBASIC_BUILD_TYPE = "ActiveX Control" Then ...

This constant gives developers precise, granular control to maximize compatibility for legacy targets without compromising modern features for other project types.

Project type compiler constant · Issue #2122 · twinbasic/twinbasic
Is your feature request related to a problem? Please describe. I only tested my ucSimplePlayer OCX in tB and VBA64 originally, so I didn’t catch the bug where it was building with LongLong in 32bit…

*The initial draft for this section was generated via Gemini-2.5-Pro.

Discord Chat Summary

* Auto-generated via Claude-Sonnet-4

Overview

This week's discussions in the twinBASIC general channel focused heavily on language compatibility features, WinDevLib improvements, and practical development challenges. The community engaged in detailed technical conversations about string conversion functions, Unicode support, and COM interface handling, while several bug reports led to immediate fixes and updates.

Language Compatibility & Unicode Support

  • waynephillipsea confirmed that CStr() conversion from Integer arrays (WCHAR) to String is not currently supported but has been mentioned for future consideration
  • The community explored Chinese character handling, with bclothier and fafalone explaining the proper use of ChrW() instead of Chr() for Unicode characters
  • fafalone demonstrated twinBASIC's advanced Unicode support, showing that identifiers can use emoji characters in types, constants, and function names
  • Discussion highlighted encoding challenges when migrating existing non-English VBx code that uses Chr() with MBCS encoding

WinDevLib Updates & Bug Fixes

  • Multiple WinDevLib issues were identified and resolved, including duplicate WSASocket declarations and missing closesocket function
  • fafalone fixed the COINIT_MULTITHREADED constant value and added missing IFilter return values from Filterr.h
  • waynephillipsea suggested exploring AI assistance for checking WinDevLib definitions, with fafalone reporting mixed but promising initial results with Claude
  • Interface method overloading and [PreserveSig] attribute usage was explained in detail for handling HRESULT returns

Development Tools & IDE Features

  • Discussion of shell context menu implementation revealed that the 30-year-old Brad Martinez approach is still necessary, though modern helpers simplify the process
  • waynephillipsea acknowledged that an Object Browser feature is planned but currently limited by development time constraints
  • A live save issue was reported and resolved through compiler restart, highlighting the new save failure detection system's effectiveness

Bug Reports & Fixes

  • Delegate functionality in classes was identified as broken in the latest beta, with a bug report filed
  • waynephillipsea addressed a driver compilation issue between versions 785 and 795 related to HRESULT checking changes
  • The new save failure detection system successfully prevented data loss during a file handling issue

Conclusion

The week demonstrated strong community collaboration in identifying and resolving compatibility issues, particularly around Unicode handling and WinDevLib functionality. The immediate response to bug reports and the detailed technical explanations provided by core contributors show the project's commitment to maintaining VB6 compatibility while adding modern features. The discussion of AI-assisted code checking suggests potential future improvements to development workflows and library maintenance.

Around the Web

WinDevLib Continues to Grow

If you're not familiar with fafalone's Windows Development Library "twinPACK", you should be. To get up to speed, check out this article I wrote on the topic last April:

WinDevLib: A Better Way to Call Windows API Functions in twinBASIC
Stop struggling to declare your Windows API calls in twinBASIC. Use the Windows Development Library twinPACK and let fafalone do all the hard work for you.

Along those lines, fafalone posted the following in the Discord show-and-tell channel (emphasis in original):

Just as a status update, WDL continues to expand all the time, so it's worth getting new versions. As of v8.12.552, WinDevLib now has: Over 10,000 APIs (~13100 - A/W variants) ~3100 COM interfaces 7200 Types, 6500 Enums, and about 20,000 Public Consts Anything missing from the common features let me know, any additional special sets you'd like covered, let me know. (A few are still on the todo list pending tB native Alias support; OpenGL and SQL, mainly)

Changelog

Here are the updates from the past week. You can also find this information by visiting the GitHub twinBASIC Releases page.

Releases · WaynePhillipsEA/twinbasic
Contribute to WaynePhillipsEA/twinbasic development by creating an account on GitHub.

AI-Generated Changelog Summary

* Auto-generated via Claude-Sonnet-4, sorted in order of its opinion of "most impactful changes."

New Build Type Compiler Constant: Added TWINBASIC_BUILD_TYPE constant for conditional compilation based on project type ("Standard EXE", "Standard DLL", "ActiveX DLL", "ActiveX Control")

Improved ActiveX Control Support: Enhanced placement and behavior of twinBASIC ActiveX controls on Access forms, including proper sizing and focus handling for multiple controls

Enhanced Type Library Features: Added [ComExport] attribute support for API declares and constants in standard modules, allowing better COM interoperability and type library exposure

Runtime Codepage Configuration: New project setting ("Project: Runtime Windows Codepage") for customizing runtime ANSI codepage used by functions like Chr(), String(), and StrConv()

IDE and Designer Improvements: Fixed form designer issues with control arrays, copy/paste operations, and font object sharing; markdown files in packages now default to preview mode

Enhanced Debugger Stability: Resolved crashes when stepping through Debug.Assert failures and improved overall debugging reliability


WARNING: The following issues are present in BETA builds 623 - 802 (the latest build as of publication):

  • IMPORTANT: This is an interim/experimental release. It includes significant changes, so some instability is to be expected. [Editor's Note: Rolling back to BETA 622 may be necessary if any of the KNOWN ISSUES below affect your project.]
  • KNOWN ISSUE: Controls are not being destroyed properly by the form designer, causing big memory leaks (broken in this build)
  • there are known memory leaks in these versions, so memory usage will be higher than at the Version 1.0 release

BETA 788

  • fixed: (regression since BETA 786) compiler performance degradation over time when large projects are edited
  • fixed: placing a tB AX control onto an Access form would not honour the original size until resized manually [ commissioned work, Tecman ]
  • fixed: AX UIDeactivate implementation to prevent focus issues with having multiple tB AX controls on Access forms [ commissioned work, Tecman ]
  • fixed: 'Override entry point' project option was broken in recent builds [ fafalone, discord ]

BETA 789

  • fixed: form designer issue of newly placed controls sharing Font object inappropriately until designer is reloaded [ fafalone, discord ]

BETA 790

  • added: support [ComExport] attribute on API declares in standard modules, for exposure in generated type libraries [ VanGoghGaming, discord ]
  • added: support [ComExport] attribute on constant fields in standard modules, for exposure in generated type libraries [ VanGoghGaming, discord ]

BETA 791

  • fixed: some expressions without a return value (using Sub/void functions) were allowed to be assigned to a variable, with errors only picked up during the final codegen stage [ XYplorer ]
  • fixed: conversions from UDT to intrinsic basic types would not show an error until final codegen stage [ XYplorer ]
  • fixed: temporarily disabled one of the recent performance improvements to help with stability [ XYplorer ]

BETA 792

  • fixed: re-enabled one of the recent performance improvements, now fixed [ XYplorer ]

BETA 793

  • improved: general compiler stability

BETA 794

  • added: project setting 'Project: Runtime Windows Codepage' for changing the runtime ANSI codepage used by functions such as Chr(), String() and StrConv(), other cases to follow [ loquat, discord ]

BETA 795

  • fixed: (regression in BETA 791) compiler crash in some instances

BETA 796

  • fixed: runtime HRESULT internal error checks now disabled in kernel compilation mode to avoid dependencies [ fafalone, discord ]

BETA 797

  • added: support for va_list ParamArray with __stdcall calling convention [ fafalone, discord ]
  • fixed: IDE internal error, Cannot set properties of undefined (setting '0') [ fafalone, discord ]
  • fixed: (regression) Menu ShortcutId could not be set in the form designer [ fafalone, discord ]

BETA 798

  • fixed: AutoSize property now doesn't auto resize if the new picture is Nothing or empty [ XYplorer, discord ]
  • improved: HScollBar and VScrollBar control implementations rewritten to better match VBx
  • fixed: (regression since BETA 786) Delegates declared in form class modules were broken [ fafalone, discord ]

BETA 799

BETA 800

BETA 801

  • fixed: using [ComExport] on a Declare Sub would produce an invalid definition in the type library [ VanGoghGaming ]
  • updated: 1 language pack (German)
  • special thanks to our community members for the new/updated language packs (Krool)

BETA 802

  • fixed: in some situations UserControl Resize event could fire before InitProperties/ReadProperties [ sokinkeso, discord ]

All original code samples by Mike Wolfe are licensed under CC BY 4.0