Exported Functions and Variables
It's possible to export a function or variable from standard modules, including with CDecl, e.g.
[DllExport]
Public Const MyExportedSymbol As Long = &H00000001
[DllExport]
Public Function MyExportedFunction(ByVal arg As Long) As Long
[DllExport]
Public Function MyCDeclExport CDecl(ByVal arg As Long)
This is primary used to create Standard DLLs (see next section), but this functionality is also available in Standard EXE and other compiled project types.
Project Configuration
Built in support for making Standard DLLs
While it was possible to accomplish this via hacks previously, tB offers it as a built in project type. You can choose this project type at startup, then you simply need to mark functions with [DllExport]
when you want them exported. The name will be used as-is, it will not be mangled. The CDecl
calling convention is supported with the normal syntax, e.g. Public Function foo CDecl(bar As Long) As Long
.
Standard DLLs in twinBASIC can still specify a startup point; each export will then check if this code has run yet, and if not, run it.
Built in support for making Console Applications
This project type allows making a true console project rather than a GUI project. Helpfully, it will also add a default Console
class for reading/writing console IO and provided debug console.
Built in support for easily making services.
tB has a services package (WinServicesLib) that makes creating full featured true services a breeze. It simplifies use of MESSAGETABLE resources, multiple services per exe, named pipes for IPC, and more. See samples 21-22.
Built in support for making Kernel-Mode Drivers.
Kernel mode drivers can only access a very limited subset of the API, and can't call usermode DLLs like a runtime. So it would typically require elaborate hacks and dramatically limit what you could do in prior BASIC products, if possible at all. And of course, there's no WOW64 layer for kernel mode, so tB is the first BASIC product to support making drivers for 64bit Windows. This is controlled by the 'Project: Native subsystem' option, as well as the following two features.
Built in support for overriding entry point.
BASIC applications typically have a hidden entry point that is the first to run, before Sub Main
or the startup form's Form_Load
. This sets up features of the app like initializing COM. twinBASIC supports overriding this and setting one of your own procedures as the true entry point. This is mostly useful for kernel mode projects, which must have a specific kind of entry point and can't call the normal APIs in the default. But there are other reasons you might want to use this option, but be warned: Many things will break in a normal application if you don't do the initialization procedures yourself or understand precisely what you can't use.
Place API declares in the IAT
tB has the option to put all API declares in the import address table rather than call them at runtime via LoadLibrary/GetProcAddress
like VBx (which puts TLB-declared APIs in the import table; tB replicates this too but further provides an option for in-project declares).
This has a small performance advantage in that it's loaded and bound at startup rather than on the first call, but the primary use is for kernel mode, which cannot call LoadLibrary
and other user mode APIs to use late binding.
Register ActiveX builds to HKEY_LOCAL_MACHINE
or HKEY_CURRENT_USER
option.
While modern applications use HKEY_CURRENT_USER
, for VBx compatibility components must be registered to HKEY_LOCAL_MACHINE
. Note that this requires running as admin when registering.
Registration at build time is optional
tB provides the Project: Register DLL after build option so you can disable automatic registration, if for example you wanted to move the file first.
Misc Attributes
The following attributes are also available but haven't been described above:
[Description("text")]
attribute for APIs, UDTs , and Consts that are shown in popups when you hover over uses of them and in VBx object browser. Additionally, this attribute can be used forModule
orClass
to describe the module/class itself, and if a class represents a creatable control, it will often be used in component lists to describe the control, as it's exported as thehelpstring
attribute at the class level too.[RunAfterBuild]
attribute-- you can specify a function that runs after your exe is built (there'sApp.LastBuildPath
to know where it is if you're e.g. signing the exe).- Per-class/module and Per-procedure
[IntegerOverflowChecks(False)]
,[FloatingPointErrorChecks(False)]
and[ArrayBoundsChecks(False)]
attributes to disable those checks on performance-critical routines while leaving them generally in place. - Constant function folding. You can specify a
[ConstantFoldable]
attribute for functions where when called with non-variable input, will be computed at compile time, rather than runtime. For example, a function to converted string literals to ANSI. The result would never change, so the resulting ANSI string is stored, rather than recomputing every run. [Unimplemented]
attribute for methods allows showing a compiler warning about it being unimplemented wherever it's called. You can upgrade it to error too.[SetDllDirectory(True/False)]
attribute to allow an explicitly loaded DLL to load it's own dependencies from it's load path. Also has the effect of allowing searching the app path for the DLLs in the base app's declare statements. It can be used per-declare or within a module.[EnumId("GUID")]
specifies a GUID to be associated with an enum in type libraries.[TypeHint()]
attribute allows populating Intellisense with an enum for types other thanLong
.[CompileIf(condition)]
method attribute for more advanced control over conditional compilation.[DebugOnly]
for a Sub/Function will exclude calls to it from the build.[DllStackCheck(False)]
attribute for DLL Declares giving minor codegen size reduction on 32-bit API calls.[Debuggable(False)]
attribute turns of breakpoints and stepping for the method or module.[PopulateFrom()]
to populate enums via JSON
Note that you can also use VBx attributes with the new syntax; [PredeclaredId]
, [Hidden]
, [Restricted]
\etc.
Standard Library Enhancements
Unicode support
Native functions that take string arguments, such as MsgBox
and FileSystem functions (e.g. Open
, Dir
, Mkdir
, Kill
, and RmDir
) now support Unicode. Additionally, .twin files make this easy to use as the editor supports Unicode as well. So you can paste a Unicode string in the editor, see it appear correctly, then have the same string correctly displayed by tB functions and controls.
Encoding options for file i/o
The Open
statement supports Unicode through the use of a new Encoding
keyword and variable, and allows you to specify a wide range of encoding options in addition to standard Unicode options.
Usage example:
Open "C:\MyFile.txt" For Input Encoding utf-8 As #1
The full list of encoding options currently defined (and don't worry, these will come up in Intellisense) is: default_system_ansi
, utf_7
, utf_7_bom
, utf_8
, utf_8_bom
, utf_16
, utf_16_bom
, us_ascii
, koi8_u
, koi8_r
, big5
, iso_8859_1_latin1
, iso_8859_2_latin2
, iso_8859_3_latin3
, iso_8859_4_latin4
, iso_8859_5_cyrillic
, iso_8859_6_arabic
, iso_8859_7_greek
, iso_8859_8_hebrew
, iso_8859_9_latin5_turkish
, iso_8859_10_latin6_nordic
, iso_8859_11_thai
, iso_8859_13_latin8_baltic
, iso_8859_14_latin8_celtic
, iso_8859_15_latin9_euro
, iso_8859_16_latin10_balkan
, windows_1250_central_europe
, windows_1251_cyrillic
, windows_1252_western
, windows_1253_greek
, windows_1254_turkish
, windows_1255_hebrew
, windows_1256_arabic
, windows_1257_baltic
, windows_1258_vietnamese
, ibm_850_western_europe
, ibm_852_central_and_eastern_europe
, ibm_855_cyrillic
, ibm_856_hebrew
, ibm_857_turkish
, ibm_858_western_europe
, ibm_860_portuguese
, ibm_861_icelandic
, ibm_862_hebrew
, ibm_863_canadian
, ibm_865_danish
, ibm_866_cyrillic
, ibm_869_greek
, ibm_932_japanese
, and ibm_949_korean
.
Others with a similar format should be accepted depending on system support.
New Built-in functions:
In addition to the new datatype-related and component name functions already described, the standard builtin VBA
library functions include:
IsArrayInitialized(variable)
- Determines if an array is initialized. Note: AVariant
declared as empty array withArray()
will returnTrue
.RGBA(r, g, b, a)
- Like theRBG()
function, only including the alpha channel.RBG_R(rgba)
,RGB_B(rgba)
,RBG_G(rgba)
, andRGBA_A(rgba)
- Get the values for individual channels.TranslateColor(ColorValue, Optional Palette)
- Translates an OLE color value to an RGB color.ProcessorArchitecture()
- Returns eithervbArchWin32
orvbArchWin64
, depending on application bitness.CallByDispId(Object, DispId, CallType, Arguments)
- Similar toCallByName()
, but uses the dispatch id instead of method name.RaiseEventByName(Object, Name, Args)
- Invokes an event on class, using arguments specified as a singleVariant
containing an array.RaiseEventByName2(Object, Name, Arg1, Arg2, ...)
- Invokes an event on class, using arguments specified as a ParamArray.PictureToByteArray(StdPicture)
- Converts a picture to a byte array; Globals.LoadPicture supports loading from byte arrays.CreateGUID()
- Returns a string with a freshly generated GUID.AllocMem(size)
andFreeMem
- allocate and free memory from the process heap.Int3Breakpoint
- Inserts a true breakpoint helpful for attached external debuggers.GetDeclaredTypeProgId(Of T)
/GetDeclaredTypeClsid(Of T)
generics for getting strings of ProgID/CLSID.GetDeclaredMinEnumValue(Of T)
/GetDeclaredMaxEnumValue(Of T)
generics- Some
Interlocked*
functions
Built in runtime functions and redirects from msvbvm60.dll
tB has built in support for some of the most commonly used runtime functions, for compatibility. These all support both 32 and 64bit. Unless otherwise noted, all of these function in two ways: First, built in native versions that are always present (unless you remove the basic compiler packages), with the most common arrangements of arguments. These don't require a Declare
statement. If you do provide a Declare
version, tB will allow whatever arrangements of arguments you specify (e.g. As Any
instead of As LongPtr
), mapped to an alias if provided.
- Memory functions:
GetMem1
,GetMem2
,GetMem4
,GetMem8
,PutMem1
,PutMem2
,PutMem4
,PutMem8
with new additionsGetMemPtr
andPutMemPtr
pegged to the current pointer size. vbaObjSet
,vbaObjSetAddref
,vbaCastObj
, andvbaObjAddref
for manipulating object assignments through pointers.vbaCopyBytes
andvbaCopyBytesZero
vbaAryMove
andvbaRefVarAry
(currently only with aDeclare
statement).- tB also has an instrinsic
VarPtr
but will still redirect calls via a declare statement, e.g. aliases used for arrays (though tB'sVarPtr
supports arrays natively).
GUI components
Support for modern image formats
You no longer face an incredibly limited format selection for images in tB Forms and Controls; not only do the Bitmap and Icon formats support the full range of formats for those, you can additionally load PNG Images, JPEG Images, Metafiles (.emf/.wmf), and SVG Vector Graphics (.svg).
Improved LoadPicture
Additionally, LoadPicture
can load all image types directly from a byte array, rather than requiring a file on disk. You can use this to load images from resource files or other sources. Note that if your projects references stdole2.tlb (most do), currently you must qualify it as Global.LoadPicture
to get tB's custom binding that supports byte arrays.
Transparency and Alpha Blending on Forms
Form.TransparencyKey
This new property specifies a color that will be transparent to the window below it in the z-order (all windows, not just in your project). Setting this property will cause the specified color to be 100% transparent. A Shape control with a solid FillStyle
is a helpful tool to color the areas of the form in the key color.
Form.Opacity
This sets an alpha blending level for the entire form. Like transparency, this is to all windows immediately underneath it. Note that any areas covered by the TransparencyKey
color will remain 100% transparent.
The following image shows a Form with a TransparencyKey
of Red, using a Shape control to define the transparent area, while also specifying 75% Opacity
for the entire form:
Additional Form features
In addition to the above, forms have:
DpiScaleX
/DpiScaleY
properties to retrieve the current values.MinWidth
,.MinHeight
,.MaxWidth
, and.MaxHeight
properties so subclassing isn't needed for thisForm.TopMost
property.- Control anchoring: control x/y/cx/cy can made relative, so they're automatically moved/resized with the Form. For example if you put a TextBox in the bottom right, then check the Right and Bottom anchors (in addition to Top and Left), the bottom right will size with the form on resize. This saves a lot of boiler-plate sizing code.
- Control docking: Controls can be fixed along one of the sides of the Form (or container), or made to fill the whole Form/container. Multiple controls can be combined and mixed/matched in docking positions.
For more information on Control Anchoring and Control Docking, see the Wiki entry Control Anchoring and Docking ‐ Automatic size and position management.
Unicode support
All tB-implemented controls support Unicode, both in the code editor and when displayed.
Important: If you subclass controls, note that this means you will receive the Unicode (W) version of window messages, e.g. ListViews will send LVN_GETDISPINFOW (LVN_FIRST - 77)
instead of LVN_GETDISPINFOA (LVN_FIRST - 50)
.
UserControl Enhancements
The UserControl object now provides the new Boolean property PreKeyEvents
that enables corresponding new events PreKeyDown
and PreKeyUp
. These allow handling special keys like tab, arrows, etc without OS or COM hooks (for example, based on the IOleInPlaceActiveObject
interface). These work with all child windows inside the UserControl, including ones created by CreateWindowEx
. You can also access raw message data in the PreKeyDown
/PreKeyUp
event handlers with the new PreKeyWParam
/PreKeyLParam
and PreKeyTargetHwnd
UserControl properties.
Control Modernization
tB will eventually replace all built in controls that you're used to, for now the ones available are: CommandButton, TextBox, ComboBox, CheckBox, OptionButton, Label, Frame, PictureBox, Line, Shape, VScrollBar, HScrollBar, Timer, DriveListBox, DirListBox, FileListBox, Image, and Data from the basic set; then, ListView, TreeView, ProgressBar, DTPicker, MonthView, Slider, and UpDown from the Common Controls.
- Controls support x64: Every control can be compiled both as 32bit and 64bit without changing anything.\
- Controls are DPI aware: They will automatically size correctly when dpi awareness is enabled for your app.\
- Controls support Visual Styles per-control: Comctl6 styles can be applied, or not, on a control-by-control basis with the
.VisualStyles
property.
Alternatives for unimplemented controls
The best option is Krool's VBCCR and VBFlexGrid projects. These are now available from the Package Server in x64-compatible form, and are also DPI aware and support Visual Styles.
Additionally, the original OCX controls provided by Microsoft will work fine; however, they're mostly 32-bit only. The x64 version of MSComCtl.ocx
doesn't come with Windows and isn't legally redistributable but if you have Office 64bit, it works in tB.
Misc additional control properties
TextBox.NumbersOnly
property: Restricts input to 0-9 by setting theES_NUMBER
style on the underlying control.TextBox.TextHint
property: Sets the light gray hint text in an empty TextBox (EM_SETCUEBANNER
).PictureDpiScaling
property for forms, usercontrols and pictureboxes: PictureDpiScaling property allows you to turn off DPI scaling of images so that they display at 1:1 rather than allowing the OS to stretch them. The idea being you may want to choose a different bitmap manually, rather than apply the somewhat limited OS-stretching.Label.VerticalAlignment
property: Defaults to Top.Label.LineSpacing
property (in twips, default is 0)Label.Angle
property (in degrees, rotates the label text)Label.BorderCustom
property (has suboptions to set size, padding and color of borders independently for each side).
New Controls
QR Code Control
Easily display custom QR codes with a native control.
Multiframe Control
This control allows you to create a number of frames within it with their size specified as a percentage, such that as the control is resized the frames within expand proportionally. For details and a video demonstration, Mike Wolfe's twinBASIC Weekly Update covered it when released.
Combined with anchors and docking, this allows designing highly functional and complex layouts visually, without writing any code to handling resizing.
Design Experience and Compiler Features
Customize COM initialization
You can specify the call used by the hidden entry point with the following options: CoInitialize STA
, CoInitializeEx MTA
, OleInitialize STA
. If you don't know the difference, don't change it from the default.
Customize symbol table parameters
You can adjust the following parameters: Max Size Raw, Max Size Lookup, and Data Type Lookup. These options allow for compiling very large projects that would otherwise have issues, and the compiler will notify you if these values need to be increased.
Sanitize Boolean types
Under the hood, a Boolean is a 2-byte type. With memory APIs, or when receiving these from outside code, it's possible to store values other than the ones representing True
and False
. This option validates Booleans from external sources, e.g. COM objects and APIs, to ensure only the two supported values are stored.
Stale/dangling pointer detection
Bugs result from using Strings and Variants after they have been freed. It may not be noticed immediately if the memory has not been overwritten, but it's sometimes hard to detect and can cause issues like a String displaying it's previous value or garbage. This debugging option detects use-after-free, and replaces the data with a special symbol indicating the problem. Below shows an example where the ListView ColumnHeader text had been set by previously-freed string and detected by this feature:
Previously, it had shown the same text for every column-- but only under certain circumstances, leading to the issue being overlooking for a long time.
Additional compiler options
Projects can be marked
LARGEADDRESSAWARE
.A manual base address can be specified
Option to strip PE relocation symbols
Exploit mitigation
You can enable the following:
Data execution prevention (DEP)
Address-space layout randomization (ASLR)
Compiler Warnings
twinBASIC provides compiler warnings during design time for common bad practices or likely oversights, including:
Warnings for likely-incorrect hex literals
Non-explicit values are coerced into the lowest possible type first. So if you declare a constant as&H8000
, the compiler sees it as an -32,768Integer
, and when you're putting that into aLong
you almost certainly do not want -32,768, you want positive 32,768, which requires you to instead use&H8000&
.
This warning is supplied for&H8000
-&HFFFF
and&H80000000
-&HFFFFFFFF
.Warnings for implicit variable creation with
ReDim
.
When you useReDim myArray(1)
, themyArray
variable is created for you, when it's good practice to declare all variables first.Warnings for use of
DefType
This feature is discouraged for it making code difficult to read and prone to difficult to debug errors.
The full list can be found in your project's Settings page:
Adjusting warnings
Each warning has the ability to set them to ignore or turn them into an error both project-wide via the Settings page, and per-module/class, and per-procedure with [IgnoreWarnings(TB___)]
, [EnforceWarnings(TB____)]
, and [EnforceErrors(TB____)]
attributes, where the underscores are replaced with the full number, e.g. [IgnoreWarnings(TB0001)]
; the leading zeroes must be included.
Strict mode
twinBASIC has added the following warning messages to support something similar to .NET's Strict Mode, where certain implicit conversions are not allowed and must be made explicit. By default, these are all set to be ignored, and must be enabled in the "Compiler Warnings" section of Project Settings or per-module/procedure with [EnforceWarnings()]
. All of these can be configured individually and ignored for procedure/module scope with [IgnoreWarnings()]
TB0018: Impicit narrowing conversion
Such as converting a Long to Integer; if you have Dim i As Integer, l As Long
then i = l
will trigger the warning, and i = CInt(l)
would be required to avoid it.
TB0019: Implicit enumeration conversion
When assigning a member of one Enum to a variabled typed as another, such as Dim day As VbDayOfWeek: day = vbBlack
. The CType(Of <type>)
operator whose use in pointers was described in the previous section is also used to specify an explicit type conversion in this case; the warning would not be triggered by day = CType(Of VbDayOfWeek)(vbBlack)
.
TB0020: Suspicious interface conversion
If a declared coclass doesn't explicitly name an interface as supported, converting to it will trigger this warning, e.g.
Dim myPic As StdPicture
Dim myFont As StdFont
Set myFont = myPic
You'd use Set myFont = CType(OfStdFont)(myPic)
to avoid this warning.
TB0021: Implicit enumeration conversion to/from numeric Triggered by assigning a numeric literal to a variabled typed as an Enum, such as Dim day As VbDayOfWeek: day = 1
. To avoid it you'd use day = CType(Of VbDayOfWeek)(1)
.
Run some Subs from the IDE
The CodeLens feature allows running Subs and Functions, with no arguments and in modules (but not classes/Forms/UserControls) right from the editor without starting the full program. It has robust access to your code; it can access constants, call other functions both instrinsic and user-define, call APIs, and print to the Debug Console.
Methods eligible to run with CodeLens (when enabled), have a bar above them that you can click to run: