The Visual Basic Runtime Library

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Visual Basic 17.13 on .NET 10 (SDK 10.0.402) and was checked on 2026-10-19. Every example is a complete console program that starts with Option Strict On, was built with warnings treated as errors (unless the text says otherwise) and was run in the .NET 10 SDK container (Linux, arm64); the output shown is real output. Several programs have a Sub Main selected with the StartupObject property in one project whose root namespace is ShelfRt. The SDK imports System, System.Collections.Generic, System.IO, System.Linq and others project-wide, and the compiler always imports Microsoft.VisualBasic, which is why Left, Len, Now, Choose and vbCrLf need no Imports line.

The Visual Basic runtime library is the assembly of helper functions, constants and types that ships with the language. It is the part of Visual Basic that old programs call most (Mid, InStr, Format, CInt, MsgBox, FileOpen, Err) and that new code should call with care: some of it is a thin layer over the BCL, some is a Visual Basic 6 compatibility layer whose behaviour differs from the BCL in one-based indexes, culture and null handling, some is Windows-only, and a little is obsolete. This page maps the library, measures what the .NET assembly really contains, and gives each area a tested example. Topics that have their own page are summarised and linked: strings on Strings and Text, conversions on Type Conversions, dates on Dates, Times and Formatting, numbers and the financial functions on Numbers and Math, and My on The My Namespace.

What the library is on .NET

On .NET Framework the whole library was Microsoft.VisualBasic.dll. On .NET it is split in two assemblies of the shared framework: Microsoft.VisualBasic.Core, which holds the runtime that every Visual Basic program references, and a small Microsoft.VisualBasic assembly next to it for code that refers to the old assembly name. The Windows Forms parts (My.Computer, the Application Framework, the dialogs behind MsgBox and InputBox) are in a third assembly, Microsoft.VisualBasic.Forms, that ships with the Windows Desktop runtime (see The My Namespace). The program below asks Microsoft.VisualBasic.Core what it exports, grouping the public types of each namespace by kind. A Visual Basic Module compiles to a sealed class marked with StandardModuleAttribute, so the program uses that attribute to recognise modules:

Option Strict On

Imports System.Reflection
Imports Microsoft.VisualBasic.CompilerServices

Module Program
    Private Function Kind(t As Type) As String
        If t.IsEnum Then Return "Enum"
        If t.IsInterface Then Return "Interface"
        If t.IsSubclassOf(GetType(MulticastDelegate)) Then Return "Delegate"
        If t.IsSubclassOf(GetType(Attribute)) Then Return "Attribute"
        If t.IsDefined(GetType(StandardModuleAttribute), False) Then Return "Module"
        If t.IsValueType Then Return "Structure"
        Return "Class"
    End Function

    Sub Main()
        Dim asm As Assembly = GetType(Microsoft.VisualBasic.Strings).Assembly
        For Each ns As String In {"Microsoft.VisualBasic", "Microsoft.VisualBasic.CompilerServices", "Microsoft.VisualBasic.FileIO"}
            Console.WriteLine("== " & ns)
            Dim groups As New SortedDictionary(Of String, List(Of String))()
            For Each t As Type In asm.GetExportedTypes()
                If t.Namespace = ns Then
                    Dim k As String = Kind(t)
                    If Not groups.ContainsKey(k) Then groups(k) = New List(Of String)()
                    groups(k).Add(t.Name)
                End If
            Next
            For Each kv In groups
                kv.Value.Sort()
                Console.WriteLine(kv.Key & ": " & String.Join(", ", kv.Value))
            Next
        Next
    End Sub
End Module
== Microsoft.VisualBasic
Attribute: ComClassAttribute, HideModuleNameAttribute, MyGroupCollectionAttribute, VBFixedArrayAttribute, VBFixedStringAttribute
Class: Collection, ControlChars, ErrObject
Enum: AppWinStyle, CallType, CompareMethod, DateFormat, DateInterval, DueDate, FileAttribute, FirstDayOfWeek, FirstWeekOfYear, MsgBoxResult, MsgBoxStyle, OpenAccess, OpenMode, OpenShare, TriState, VariantType, VbStrConv
Module: Constants, Conversion, DateAndTime, FileSystem, Financial, Information, Interaction, Strings, VBMath
Structure: SpcInfo, TabInfo
== Microsoft.VisualBasic.CompilerServices
Attribute: DesignerGeneratedAttribute, OptionCompareAttribute, OptionTextAttribute, StandardModuleAttribute
Class: BooleanType, ByteType, CharArrayType, CharType, Conversions, DateType, DecimalType, DoubleType, ForLoopControl, IncompleteInitialization, IntegerType, LateBinding, LikeOperator, LongType, NewLateBinding, ObjectFlowControl, ObjectType, Operators, ProjectData, ShortType, SingleType, StaticLocalInitFlag, StringType, Utils, Versioned
== Microsoft.VisualBasic.FileIO
Class: FileSystem, MalformedLineException, SpecialDirectories, TextFieldParser
Enum: DeleteDirectoryOption, FieldType, RecycleOption, SearchOption, UICancelOption, UIOption

So the library is small: nine modules, three classes (Collection, ControlChars, ErrObject), seventeen enumerations, five attributes and two structures in Microsoft.VisualBasic; a set of compiler helper classes in CompilerServices; and TextFieldParser, FileSystem, SpecialDirectories and their option enumerations in FileIO. Everything that follows is an instance of this list.

Module or type Contents Usually better today

Strings

Asc, AscW, Chr, ChrW, Filter, Format, FormatCurrency, FormatNumber, FormatPercent, GetChar, InStr, InStrRev, Join, LCase, Left, Len, LSet, LTrim, Mid, Replace, Right, RSet, RTrim, Space, Split, StrComp, StrConv, StrDup, StrReverse, Trim, UCase

String methods, interpolation, StringBuilder

Conversion

CTypeDynamic, ErrorToString, Fix, Hex, Int, Oct, Str, Val

Math.Truncate, Math.Floor, Convert.ToString(n, 16), TryParse

DateAndTime

Now, Today, TimeOfDay, Timer, DateAdd, DateDiff, DatePart, DateSerial, TimeSerial, DateValue, TimeValue, Day, Month, Year, Hour, Minute, Second, Weekday, WeekdayName, MonthName

DateTime, DateTimeOffset, TimeProvider, Stopwatch

Interaction

MsgBox, InputBox, Shell, CallByName, Choose, Switch, Partition, Environ, Command, Beep, GetSetting, SaveSetting, DeleteSetting, GetAllSettings, CreateObject, GetObject, AppActivate

Process.Start, Environment, reflection, If/Select Case, MessageBox

Information

IsNumeric, IsDate, IsArray, IsDBNull, IsError, IsNothing, IsReference, TypeName, VarType, SystemTypeName, VbTypeName, LBound, UBound, RGB, QBColor, Err

TryParse, Is Nothing, TypeOf ... Is, GetType

FileSystem

FileOpen, FileClose, Print, PrintLine, Write, WriteLine, Input, LineInput, EOF, LOF, Loc, Seek, FileGet, FilePut, FreeFile, Dir, MkDir, RmDir, Kill, FileCopy, FileLen, GetAttr, Rename, CurDir, ChDir

System.IO

Financial

DDB, FV, IPmt, IRR, MIRR, NPer, NPV, Pmt, PPmt, PV, Rate, SLN, SYD

keep (no BCL equivalent), but use Decimal for money

VBMath

Rnd, Randomize

Random, RandomNumberGenerator

Constants

vbCrLf, vbTab, vbNullString, vbObjectError and the constants of the enumerations

Environment.NewLine, enumerations by name

Collection

the one-based, keyed VB6 collection

List(Of T), Dictionary(Of TKey, TValue)

ErrObject

the Err object

exceptions

ControlChars

Cr, CrLf, Lf, NewLine, Tab, Quote, NullChar, Back, FormFeed, VerticalTab

same, or Environment.NewLine

(The member lists are the documented ones for the classic modules; the next program measures the counts.)

What is hidden, obsolete or Windows-only

The assembly records its own warnings as attributes, and the compiler and analyzers act on them, so reflection can list them. The program below goes through every public static member of the nine modules and counts those hidden from IntelliSense, marked obsolete, annotated as supported only on Windows (SupportedOSPlatform) or unsupported on some platforms, or marked as unsafe for trimming (RequiresUnreferencedCode). It then prints each obsolete member with its message, and each platform annotation. (In Visual Basic a variable declared in a loop body keeps its value between iterations, so the counters are re-initialised at the top of the loop body.)

Option Strict On

Imports System.ComponentModel
Imports System.Reflection
Imports System.Runtime.Versioning
Imports Microsoft.VisualBasic.CompilerServices

Module Program
    Sub Main()
        Dim asm As Assembly = GetType(Microsoft.VisualBasic.Strings).Assembly
        Dim flags As BindingFlags = BindingFlags.Public Or BindingFlags.Static Or BindingFlags.DeclaredOnly
        Console.WriteLine("module            members  hidden  obsolete  unsupported-os  supported-os  needs-unreferenced-code")
        For Each t As Type In asm.GetExportedTypes().OrderBy(Function(x) x.Name)
            If t.Namespace = "Microsoft.VisualBasic" AndAlso t.IsDefined(GetType(StandardModuleAttribute), False) Then
                Dim total As Integer = 0, hidden As Integer = 0, obs As Integer = 0
                Dim unsup As Integer = 0, sup As Integer = 0, ruc As Integer = 0
                For Each m As MemberInfo In t.GetMembers(flags)
                    If TypeOf m Is MethodInfo AndAlso DirectCast(m, MethodInfo).IsSpecialName Then Continue For
                    total += 1
                    Dim eb = m.GetCustomAttribute(Of EditorBrowsableAttribute)()
                    If eb IsNot Nothing AndAlso eb.State = EditorBrowsableState.Never Then hidden += 1
                    If m.IsDefined(GetType(ObsoleteAttribute), False) Then obs += 1
                    If m.IsDefined(GetType(UnsupportedOSPlatformAttribute), False) Then unsup += 1
                    If m.IsDefined(GetType(SupportedOSPlatformAttribute), False) Then sup += 1
                    If m.IsDefined(GetType(System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute), False) Then ruc += 1
                Next
                Console.WriteLine(t.Name.PadRight(18) & total.ToString().PadLeft(5) & hidden.ToString().PadLeft(8) & obs.ToString().PadLeft(9) & unsup.ToString().PadLeft(13) & sup.ToString().PadLeft(14) & ruc.ToString().PadLeft(14))
            End If
        Next
        Console.WriteLine("--- obsolete members")
        For Each t As Type In asm.GetExportedTypes().OrderBy(Function(x) x.Name)
            For Each m As MemberInfo In t.GetMembers(BindingFlags.Public Or BindingFlags.Static Or BindingFlags.Instance Or BindingFlags.DeclaredOnly)
                Dim o = m.GetCustomAttribute(Of ObsoleteAttribute)()
                If o IsNot Nothing Then
                    Dim msg As String = If(o.Message, "")
                    Console.WriteLine(t.Name & "." & m.Name & " : " & msg)
                End If
            Next
            Dim ot = t.GetCustomAttribute(Of ObsoleteAttribute)()
            If ot IsNot Nothing Then Console.WriteLine("TYPE " & t.Name & " : " & ot.Message)
        Next
        Console.WriteLine("--- platform attributes")
        For Each t As Type In asm.GetExportedTypes().OrderBy(Function(x) x.Name)
            For Each m As MemberInfo In t.GetMembers(BindingFlags.Public Or BindingFlags.Static Or BindingFlags.Instance Or BindingFlags.DeclaredOnly)
                For Each a In m.GetCustomAttributes(False)
                    If TypeOf a Is OSPlatformAttribute Then
                        Console.WriteLine(t.Name & "." & m.Name & " : " & a.GetType().Name & " " & DirectCast(a, OSPlatformAttribute).PlatformName)
                    End If
                Next
            Next
        Next
    End Sub
End Module

Condensed output (the full platform list repeats an entry for every overload):

module            members  hidden  obsolete  unsupported-os  supported-os  needs-unreferenced-code
Constants           102       0        1            0             0             0
Conversion           40       0        0            0             0             2
DateAndTime          25       0        0            0             0             0
FileSystem           83       0        1            7             5             7
Financial            13       0        0            0             0             0
Information          17       0        0            0             0             0
Interaction          20       0        0            0             7             3
Strings              57       0        0            0             1             1
VBMath                4       0        0            0             0             0
--- obsolete members
Constants.vbNewLine : vbNewLine has been deprecated. For a carriage return and line feed, use vbCrLf. For the current platform's newline, use System.Environment.NewLine.
Conversions.FallbackUserDefinedConversion : FallbackUserDefinedConversion has been deprecated and is not supported.
FileSystem.FilePut : FileSystem.FilePut has been deprecated. Use FilePutObject to write Object types, or coerce FileNumber and RecordNumber to Integer for writing non-Object types.
MalformedLineException.GetObjectData : This API supports obsolete formatter-based serialization. It should not be called or extended by application code.
NewLateBinding.FallbackCall : FallbackCall has been deprecated and is not supported.
NewLateBinding.FallbackInvokeDefault1 : FallbackInvokeDefault1 has been deprecated and is not supported.
NewLateBinding.FallbackInvokeDefault2 : FallbackInvokeDefault2 has been deprecated and is not supported.
NewLateBinding.FallbackGet : FallbackGet has been deprecated and is not supported.
NewLateBinding.FallbackIndexSetComplex : FallbackIndexSetComplex has been deprecated and is not supported.
NewLateBinding.FallbackIndexSet : FallbackIndexSet has been deprecated and is not supported.
NewLateBinding.FallbackSetComplex : FallbackSetComplex has been deprecated and is not supported.
NewLateBinding.FallbackSet : FallbackSet has been deprecated and is not supported.
Operators.FallbackInvokeUserDefinedOperator : FallbackInvokeUserDefinedOperator has been deprecated and is not supported.
--- platform attributes (member: annotation)
DateAndTime.set_Today: SupportedOSPlatform windows
DateAndTime.set_TimeOfDay: SupportedOSPlatform windows
DateAndTime.set_TimeString: SupportedOSPlatform windows
DateAndTime.set_DateString: SupportedOSPlatform windows
FileSystem.ChDrive: SupportedOSPlatform windows
FileSystem.CurDir: SupportedOSPlatform windows
FileSystem.Dir: SupportedOSPlatform windows
FileSystem.InputString: UnsupportedOSPlatform ios, UnsupportedOSPlatform macos, UnsupportedOSPlatform tvos
FileSystem.Lock: UnsupportedOSPlatform ios, UnsupportedOSPlatform macos, UnsupportedOSPlatform tvos
FileSystem.Unlock: UnsupportedOSPlatform ios, UnsupportedOSPlatform macos, UnsupportedOSPlatform tvos
FileSystem.Rename: SupportedOSPlatform windows
Interaction.Beep: SupportedOSPlatform windows
Interaction.DeleteSetting: SupportedOSPlatform windows
Interaction.GetAllSettings: SupportedOSPlatform windows
Interaction.GetSetting: SupportedOSPlatform windows
Interaction.SaveSetting: SupportedOSPlatform windows
Interaction.CreateObject: SupportedOSPlatform windows
Interaction.GetObject: SupportedOSPlatform windows
Strings.StrConv: SupportedOSPlatform windows

What this means in practice:

  • Obsolete. Only two public members of the nine modules are obsolete: the constant vbNewLine and the FilePut overload that takes an Object. The Fallback... members of NewLateBinding and Operators are compiler plumbing that must not be called. Both obsolete members are shown in "Constants" below.

  • Windows-only. Beep, the registry settings functions (GetSetting, SaveSetting, DeleteSetting, GetAllSettings), CreateObject/GetObject (COM), StrConv, Dir, Rename, CurDir, ChDrive and the setters of Today, TimeOfDay, DateString and TimeString carry SupportedOSPlatform("windows"). A call from code that can run elsewhere makes the analyzer rule CA1416 report them, and with TreatWarningsAsErrors the build fails (see the example in "Constants" below). Lock, Unlock and InputString are unsupported on iOS, macOS and tvOS. An annotation is a warning that the call may not work, not proof that it does not: Dir worked in the file example below on Linux, while Rename threw and Shell, MsgBox and InputBox have no annotation but threw too.

  • Not trim-safe. Conversion.CTypeDynamic (two overloads), Interaction.CallByName, CreateObject, GetObject, FileGet, FilePut, FileGetObject, FilePutObject, Input and the Len overload for Object are marked RequiresUnreferencedCode, because they use reflection; avoid them in programs that are trimmed or compiled ahead of time (see Early and Late Binding).

  • Hidden. None of the module members is hidden from IntelliSense, but all 29 types of Microsoft.VisualBasic.CompilerServices are (see below).

Strings

The Strings module is the VB6 string library: functions with one-based positions, tolerant of Nothing, with an optional Compare argument that follows Option Compare. The BCL equivalents are zero-based and throw on Nothing. Both can be right; the point is to know which one a piece of code uses. The details of every function are on Strings and Text; this program shows the differences that cause bugs:

Option Strict On

Imports System.Globalization

Module StringsTour
    Private Sub Try1(label As String, action As Func(Of String))
        Try
            Console.WriteLine(label & " = " & action())
        Catch ex As Exception
            Console.WriteLine(label & " -> " & ex.GetType().Name & ": " & ex.Message)
        End Try
    End Sub

    Sub Main()
        Dim title As String = "The Left Hand of Darkness"
        Dim missing As String = Nothing

        Console.WriteLine("-- one-based positions")
        Console.WriteLine("InStr(title, ""Left"")        = " & InStr(title, "Left").ToString())
        Console.WriteLine("title.IndexOf(""Left"")       = " & title.IndexOf("Left").ToString())
        Console.WriteLine("Mid(title, 5, 4)            = " & Mid(title, 5, 4))
        Console.WriteLine("title.Substring(4, 4)       = " & title.Substring(4, 4))
        Console.WriteLine("Left / Right                = " & Left(title, 3) & " / " & Right(title, 8))
        Console.WriteLine("InStr(6, title, ""e"")        = " & InStr(6, title, "e").ToString())
        Console.WriteLine("InStrRev(title, ""e"")        = " & InStrRev(title, "e").ToString())

        Console.WriteLine("-- Nothing is tolerated")
        Console.WriteLine("Len(Nothing)                = " & Len(missing).ToString())
        Console.WriteLine("UCase(Nothing) is empty     = " & (UCase(missing).Length = 0).ToString())
        Console.WriteLine("Left(Nothing, 3) is empty   = " & (Left(missing, 3).Length = 0).ToString())
        Try1("missing.Length", Function() missing.Length.ToString())

        Console.WriteLine("-- but bad arguments still throw")
        Try1("Mid(title, 0)", Function() Mid(title, 0))
        Try1("Left(title, -1)", Function() Left(title, -1))
        Try1("title.Substring(40)", Function() title.Substring(40))
        Console.WriteLine("Left(title, 100) = " & Left(title, 100))
        Console.WriteLine("Mid(title, 100)  = '" & Mid(title, 100) & "'")

        Console.WriteLine("-- whitespace and padding")
        Console.WriteLine("[" & Trim("  a b  ") & "] [" & LTrim("  a") & "] [" & RTrim("a  ") & "]")
        Console.WriteLine("Trim leaves tabs: " & (Trim(vbTab & "a" & vbTab).Length).ToString() & " vs String.Trim: " & (vbTab & "a" & vbTab).Trim().Length.ToString())
        Console.WriteLine("[" & Space(3) & "] [" & StrDup(3, "ab") & "] [" & StrReverse("Dune") & "]")
        Console.WriteLine("[" & LSet("ab", 5) & "] [" & RSet("ab", 5) & "]")

        Console.WriteLine("-- characters and codes")
        Console.WriteLine("Asc(""A"") = " & Asc("A").ToString() & ", Chr(66) = " & Chr(66) & ", AscW(""é"") = " & AscW("é").ToString() & ", ChrW(&H263A) = " & ChrW(&H263A))

        Console.WriteLine("-- Replace, Split, Join, Filter")
        Console.WriteLine(Replace(title, "the", "A", 1, 1, CompareMethod.Text))
        Console.WriteLine(Replace("a-b-c-d", "-", "+", 3))
        Dim parts As String() = Split("Dune;Emma;;Ulysses", ";")
        Console.WriteLine(parts.Length.ToString() & " parts, Join = " & Join(parts, "|"))
        Console.WriteLine("Filter: " & Join(Filter(parts, "e", True, CompareMethod.Text), ","))

        Console.WriteLine("-- formatting helpers (en-US)")
        CultureInfo.CurrentCulture = New CultureInfo("en-US")
        Console.WriteLine(FormatNumber(1234567.891, 2) & " | " & FormatCurrency(19.5) & " | " & FormatPercent(0.256, 1) & " | " & Format(1234.5, "#,##0.00"))
        Console.WriteLine(FormatNumber(-3.5, 1, TriState.UseDefault, TriState.True) & " | " & FormatNumber(0.5, 2, TriState.False))
    End Sub
End Module
-- one-based positions
InStr(title, "Left")        = 5
title.IndexOf("Left")       = 4
Mid(title, 5, 4)            = Left
title.Substring(4, 4)       = Left
Left / Right                = The / Darkness
InStr(6, title, "e")        = 6
InStrRev(title, "e")        = 23
-- Nothing is tolerated
Len(Nothing)                = 0
UCase(Nothing) is empty     = True
Left(Nothing, 3) is empty   = True
missing.Length -> NullReferenceException: Object reference not set to an instance of an object.
-- but bad arguments still throw
Mid(title, 0) -> ArgumentException: Argument 'Start' must be greater than zero. (Parameter 'Start')
Left(title, -1) -> ArgumentException: Argument 'Length' must be greater or equal to zero. (Parameter 'Length')
title.Substring(40) -> ArgumentOutOfRangeException: startIndex cannot be larger than length of string. (Parameter 'startIndex')
Left(title, 100) = The Left Hand of Darkness
Mid(title, 100)  = ''
-- whitespace and padding
[a b] [a] [a]
Trim leaves tabs: 3 vs String.Trim: 1
[   ] [aaa] [enuD]
[ab   ] [   ab]
-- characters and codes
Asc("A") = 65, Chr(66) = B, AscW("é") = 233, ChrW(&H263A) = ☺
-- Replace, Split, Join, Filter
A Left Hand of Darkness
b+c+d
4 parts, Join = Dune|Emma||Ulysses
Filter: Dune,Emma,Ulysses
-- formatting helpers (en-US)
1,234,567.89 | $19.50 | 25.6% | 1,234.50
(3.5) | .50

Source: Strings module

Points the output makes concrete:

  • InStr returns 5 where IndexOf returns 4, and InStr returns 0 for "not found" where IndexOf returns -1. Mid(title, 5, 4) and Substring(4, 4) are the same slice.

  • Len, UCase, Left and the other functions treat Nothing as the empty string; the member call missing.Length throws NullReferenceException.

  • The functions still validate their arguments: a start of 0 or a negative length throws ArgumentException with the VB-style message "Argument 'Start' must be greater than zero.". But a length that is too long is clipped (Left(title, 100)), and a start beyond the end gives an empty string, where Substring throws.

  • Trim, LTrim and RTrim remove spaces only: the string with tabs around a keeps length 3, where String.Trim() gives 1.

  • Replace(expression, find, replacement, start, count, compare) with a start greater than 1 returns only the text from start: Replace("a-b-c-d", "-", "+", 3) printed b+c+d, and the first characters are gone. This is the most surprising function in the module.

  • Format, FormatNumber, FormatCurrency and FormatPercent use the current culture, which is why the program sets en-US first (in the invariant culture the currency symbol is a generic sign). The TriState arguments choose the leading zero, the parentheses for negative numbers and the grouping.

  • StrConv with VbStrConv.ProperCase and the Japanese and Chinese conversions is annotated as Windows-only; TextInfo.ToTitleCase is the cross-platform way.

  • The module has Space, StrDup, StrReverse and LSet/RSet (fixed-width padding), which have no one-word BCL equivalent. StrReverse reverses UTF-16 code units, so it breaks surrogate pairs and combining marks.

Conversion

The Conversion module has the number-to-text and number-to-integer helpers of VB6. They interact with the conversion keywords (CInt, CStr, CDate, which are compiler features, not functions of this module) described on Type Conversions.

Option Strict On

Module ConversionTour
    Sub Main()
        Console.WriteLine("-- Val reads the leading number only, always with the invariant format")
        Console.WriteLine(Val("1234abc").ToString() & " " & Val("  3.5e2xyz").ToString() & " " & Val("&H1F").ToString() & " " & Val("&O17").ToString() & " " & Val("abc").ToString() & " " & Val("1,5").ToString())
        Console.WriteLine("-- Str keeps a sign column")
        Console.WriteLine("[" & Str(42) & "] [" & Str(-42) & "] [" & CStr(42) & "]")
        Console.WriteLine("-- Hex and Oct")
        Console.WriteLine(Hex(255) & " " & Hex(-1) & " " & Oct(8) & " " & Hex(CByte(10)))
        Console.WriteLine("-- Fix and Int")
        Console.WriteLine(Fix(-2.7).ToString() & " " & Int(-2.7).ToString() & " " & Fix(2.7).ToString() & " " & Int(2.7).ToString())
        Console.WriteLine("-- CInt rounds to even; CType from Object")
        Console.WriteLine(CInt(2.5).ToString() & " " & CInt(3.5).ToString() & " " & CInt(CObj("12")).ToString())
        Console.WriteLine("-- CTypeDynamic converts at run time")
        Console.WriteLine(CTypeDynamic(Of Integer)("77").ToString() & " " & CTypeDynamic(Of Date)("2026-10-19").ToString("yyyy-MM-dd"))
        Console.WriteLine("-- ErrorToString")
        Console.WriteLine(ErrorToString(5))
        Console.WriteLine(ErrorToString(11))
        Console.WriteLine(ErrorToString(0).Length.ToString())
    End Sub
End Module
-- Val reads the leading number only, always with the invariant format
1234 350 31 15 0 1
-- Str keeps a sign column
[ 42] [-42] [42]
-- Hex and Oct
FF FFFFFFFF 10 A
-- Fix and Int
-2 -3 2 2
-- CInt rounds to even; CType from Object
2 4 12
-- CTypeDynamic converts at run time
77 2026-10-19
-- ErrorToString
Procedure call or argument is not valid.
Division by zero.
0
  • Val reads the leading characters that look like a number, never throws, always uses the period as the decimal separator, and understands &H and &O prefixes. Val("1,5") is 1, not 1.5, and Val("abc") is 0 with no way to tell "not a number" from zero. Use Double.TryParse with a culture when you must distinguish the cases.

  • Str reserves a column for the sign, so positive numbers get a leading space; CStr does not. Code that compares Str(42) with "42" fails.

  • Hex(-1) is FFFFFFFF because -1 is an Integer; the result depends on the argument’s type. Fix truncates toward zero and Int rounds down. CInt(2.5) is 2 and CInt(3.5) is 4: banker’s rounding.

  • ErrorToString(n) gives the VB6 message for an error number; 0 gives an empty string.

  • CTypeDynamic converts at run time by the type’s IConvertible or conversion operators and is marked as not trim-safe.

DateAndTime

The module holds the VB date functions and the properties Now, Today, TimeOfDay, Timer, DateString and TimeString. Most of them are shorter spellings of DateTime members; DateAdd, DateDiff and DatePart take an interval, either the DateInterval enumeration or the VB6 string codes ("d", "m", "yyyy").

Option Strict On

Imports System.Globalization

Module DateTour
    Sub Main()
        CultureInfo.CurrentCulture = New CultureInfo("en-US")
        Dim d As Date = #10/19/2026 2:30:00 PM#
        Console.WriteLine("-- DateAdd, DateDiff, DatePart")
        Console.WriteLine(DateAdd(DateInterval.Month, 3, d).ToString("yyyy-MM-dd"))
        Console.WriteLine(DateAdd("d", -19, d).ToString("yyyy-MM-dd"))
        Console.WriteLine(DateDiff(DateInterval.Day, #12/31/2025#, #1/1/2026#).ToString() & " day, " & DateDiff(DateInterval.Year, #12/31/2025#, #1/1/2026#).ToString() & " year, " & DateDiff(DateInterval.Month, #12/31/2025#, #1/1/2026#).ToString() & " month")
        Console.WriteLine("Weekday difference: " & DateDiff(DateInterval.Weekday, #10/19/2026#, #10/26/2026#).ToString())
        Console.WriteLine("Quarter " & DatePart(DateInterval.Quarter, d).ToString() & ", day of year " & DatePart(DateInterval.DayOfYear, d).ToString() & ", week " & DatePart(DateInterval.WeekOfYear, d).ToString())
        Console.WriteLine("-- Day names depend on the culture and on the first day of the week")
        Console.WriteLine(Weekday(d).ToString() & " " & Weekday(d, FirstDayOfWeek.Monday).ToString() & " " & WeekdayName(Weekday(d), False) & " " & WeekdayName(Weekday(d), True) & " " & MonthName(Month(d)))
        Console.WriteLine("-- DateSerial and TimeSerial overflow into the next unit")
        Console.WriteLine(DateSerial(2026, 14, 35).ToString("yyyy-MM-dd") & " " & TimeSerial(25, 70, 0).ToString("HH:mm") & " " & DateSerial(2026, 3, 0).ToString("yyyy-MM-dd"))
        Console.WriteLine("-- DateValue, TimeValue, Format, Timer")
        Console.WriteLine(DateValue("October 19, 2026").ToString("yyyy-MM-dd") & " " & TimeValue("2:30 PM").ToString("HH:mm") & " " & Format(d, "dddd d MMMM yyyy") & " " & Format(d, "Long Date"))
        Console.WriteLine((Timer >= 0 AndAlso Timer < 86400).ToString() & " " & Hour(d).ToString() & ":" & Minute(d).ToString() & ":" & Second(d).ToString() & " " & (Now > d).ToString() & " " & (Today = Now.Date).ToString())
        Console.WriteLine("-- IsDate")
        Console.WriteLine(IsDate("2026-10-19").ToString() & " " & IsDate("19/10/2026").ToString() & " " & IsDate("10/19/2026").ToString() & " " & IsDate("not a date").ToString())
    End Sub
End Module
-- DateAdd, DateDiff, DatePart
2027-01-19
2026-09-30
1 day, 1 year, 1 month
Weekday difference: 1
Quarter 4, day of year 292, week 43
-- Day names depend on the culture and on the first day of the week
2 1 Monday Mon October
-- DateSerial and TimeSerial overflow into the next unit
2027-03-07 02:10 2026-02-28
-- DateValue, TimeValue, Format, Timer
2026-10-19 14:30 Monday 19 October 2026 Monday, October 19, 2026
True 14:30:0 True True
-- IsDate
True False True False
  • DateDiff counts boundaries crossed, not elapsed time: from 31 December to 1 January it reports 1 day, 1 year and 1 month. For elapsed time subtract the dates and use the TimeSpan.

  • DateDiff(DateInterval.Weekday, ...) counts how many times the weekday of the first date occurs, so Monday to the next Monday is 1.

  • DateSerial(2026, 14, 35) does not throw: months and days overflow into the next unit (14 is February of 2027, and the 35th rolls into March). DateSerial(2026, 3, 0) is the last day of February. This helps for "last day of the month" and hides typos.

  • Weekday is one-based with Sunday as 1 unless the FirstDayOfWeek argument says otherwise (Monday gives 1 for a Monday); WeekdayName and MonthName use the current culture.

  • IsDate("19/10/2026") is False under en-US: the result depends on the culture. Use DateTime.TryParseExact for a fixed format.

  • The setters of Today, TimeOfDay, DateString and TimeString change the system clock and are Windows-only; the program above only reads them. Timer is the number of seconds since midnight, which wraps; use Stopwatch for measuring.

Interaction

Interaction is the grab bag: dialogs, process start, environment, late binding and a few selection functions.

Option Strict On

Imports System.Globalization

Public Class Book
    Public Property Title As String = "Dune"
    Public Property Pages As Integer = 412

    Public Function Describe(prefix As String) As String
        Return prefix & Title & " (" & Pages.ToString() & " pages)"
    End Function
End Class

Module InteractionTour
    Private Function Boom() As String
        Console.WriteLine("  (Boom was evaluated)")
        Return "boom"
    End Function

    Sub Main(args As String())
        Console.WriteLine("-- Choose: one-based, every argument is evaluated")
        Dim kind As Integer = 2
        Console.WriteLine(Choose(kind, "Hardcover", "Paperback", Boom()))
        Console.WriteLine("out of range gives Nothing: " & (Choose(5, "a", "b") Is Nothing).ToString())

        Console.WriteLine("-- Switch: first True expression wins, all are evaluated")
        Dim pages As Integer = 412
        Dim size As Object = Interaction.Switch(pages < 100, "short", pages < 300, "medium", pages >= 300, "long")
        Console.WriteLine(size)
        Console.WriteLine("no match gives Nothing: " & (Interaction.Switch(pages < 0, "negative") Is Nothing).ToString())

        Console.WriteLine("-- Command and Environ")
        Console.WriteLine("Command() = '" & Command() & "' (" & args.Length.ToString() & " arguments)")
        Environment.SetEnvironmentVariable("SHELF_MODE", "demo")
        Console.WriteLine("Environ(""SHELF_MODE"") = " & Environ("SHELF_MODE"))
        Console.WriteLine("Environ(""SHELF_MISSING"") = '" & Environ("SHELF_MISSING") & "'")
        Console.WriteLine("Environ(1) contains '=' : " & Environ(1).Contains("=").ToString())

        Console.WriteLine("-- CallByName: late-bound access by name")
        Dim b As New Book()
        CallByName(b, "Pages", CallType.Let, 500)
        Console.WriteLine(CStr(CallByName(b, "Title", CallType.Get)) & " " & CStr(CallByName(b, "Pages", CallType.Get)))
        Console.WriteLine(CStr(CallByName(b, "Describe", CallType.Method, "Book: ")))
        Try
            CallByName(b, "Missing", CallType.Get)
        Catch ex As MissingMemberException
            Console.WriteLine(ex.GetType().Name & ": " & ex.Message)
        End Try

        Console.WriteLine("-- Partition")
        Console.WriteLine(Partition(412, 0, 1000, 250) & " | " & Partition(5, 0, 100, 10) & " | " & Partition(-3, 0, 100, 10) & " | " & Partition(250, 0, 1000, 250))

        Console.WriteLine("-- Shell")
        Try
            Dim id As Integer = Shell("/bin/echo started", AppWinStyle.Hide, False)
            Console.WriteLine("Shell returned a process id: " & (id > 0).ToString())
        Catch ex As Exception
            Console.WriteLine(ex.GetType().Name & ": " & ex.Message)
        End Try
    End Sub
End Module
-- Choose: one-based, every argument is evaluated
  (Boom was evaluated)
Paperback
out of range gives Nothing: True
-- Switch: first True expression wins, all are evaluated
long
no match gives Nothing: True
-- Command and Environ
Command() = 'a "b c"' (2 arguments)
Environ("SHELF_MODE") = demo
Environ("SHELF_MISSING") = ''
Environ(1) contains '=' : True
-- CallByName: late-bound access by name
Dune 500
Book: Dune (500 pages)
MissingMemberException: Public member 'Missing' on type 'Book' not found.
-- Partition
 250: 499 |   0:  9 |    : -1 |  250: 499
-- Shell
PlatformNotSupportedException: Method requires System.Windows.Forms.
  • The module name conflicts with a type of System.Diagnostics: the plain name Switch is the Switch class (the SDK imports System.Diagnostics), and Switch(...) is rejected with BC30109 ("'Switch' is a class type and cannot be used as an expression"). Write Interaction.Switch.

  • Choose and Interaction.Switch evaluate every argument, as the message printed by Boom() shows, and return Nothing (not an exception) when the index is out of range or no condition is true. The If operator and Select Case evaluate only what they need. Choose is one-based.

  • Command() returns the arguments joined into one string (a "b c"), already in a form that has lost the original splitting; take the args parameter of Main instead.

  • Environ("NAME") returns an empty string for a missing variable; Environ(1) is the first variable by index and is "NAME=value". Environment.GetEnvironmentVariable returns Nothing when missing.

  • CallByName is late binding by name with CallType.Get, Let, Set and Method. A missing member throws MissingMemberException. It is slower than a direct call and unsafe for trimming; see Early and Late Binding.

  • Partition builds the label of the bucket that contains a number (" 250: 499"), padded to the width of the largest number.

  • Shell threw PlatformNotSupportedException on Linux; use Process.Start (see Best Practices and Common Pitfalls). MsgBox and InputBox need Windows Forms (the dialogs example is on The My Namespace).

  • GetSetting, SaveSetting, DeleteSetting, GetAllSettings, CreateObject, GetObject and Beep are Windows-only. CreateObject("Excel.Application") is the COM route described on COM Interop and Office Automation.

Information

Option Strict On

Imports System.Globalization

Module InformationTour
    Sub Main()
        CultureInfo.CurrentCulture = New CultureInfo("en-US")
        Console.WriteLine("-- IsNumeric accepts more than Double.TryParse")
        For Each s As String In {"42", " 42 ", "-1.5", "1e3", "$12", "(5)", "&HFF", "&O17", "1,000", "12abc", ""}
            Dim parsed As Double
            Console.WriteLine("IsNumeric(""" & s & """) = " & IsNumeric(s).ToString().PadRight(5) & "  Double.TryParse = " & Double.TryParse(s, parsed).ToString())
        Next
        Console.WriteLine("-- IsNumeric on non-strings")
        Console.WriteLine(IsNumeric(5).ToString() & " " & IsNumeric(True).ToString() & " " & IsNumeric(#1/1/2026#).ToString() & " " & IsNumeric("5"c).ToString() & " " & IsNumeric(Nothing).ToString())

        Console.WriteLine("-- TypeName, VarType, VbTypeName")
        Dim values As Object() = {1, 2L, 3.5, 4.5F, 5D, "text", "c"c, True, #1/1/2026#, Nothing, New Integer() {1, 2}, New List(Of String)(), DBNull.Value, CByte(1)}
        For Each v As Object In values
            Console.WriteLine(TypeName(v).PadRight(16) & VarType(v).ToString().PadRight(10) & If(v Is Nothing, "(Nothing)", v.GetType().Name))
        Next

        Console.WriteLine("-- the Is functions")
        Dim arr As Integer() = {10, 20, 30}
        Console.WriteLine("IsArray " & IsArray(arr).ToString() & ", IsNothing " & IsNothing(Nothing).ToString() & ", IsDBNull " & IsDBNull(DBNull.Value).ToString() & ", IsReference " & IsReference("x").ToString() & "/" & IsReference(5).ToString() & ", IsError " & IsError(New ArgumentException()).ToString())
        Console.WriteLine("LBound " & LBound(arr).ToString() & ", UBound " & UBound(arr).ToString())
        Console.WriteLine("-- RGB and QBColor return BGR integers")
        Console.WriteLine(Hex(RGB(255, 128, 0)) & " " & Hex(QBColor(12)))
        Console.WriteLine(SystemTypeName("Integer") & " " & VbTypeName("Int32") & " " & SystemTypeName("Boolean"))
    End Sub
End Module
-- IsNumeric accepts more than Double.TryParse
IsNumeric("42") = True   Double.TryParse = True
IsNumeric(" 42 ") = True   Double.TryParse = True
IsNumeric("-1.5") = True   Double.TryParse = True
IsNumeric("1e3") = True   Double.TryParse = True
IsNumeric("$12") = True   Double.TryParse = False
IsNumeric("(5)") = True   Double.TryParse = False
IsNumeric("&HFF") = True   Double.TryParse = False
IsNumeric("&O17") = True   Double.TryParse = False
IsNumeric("1,000") = True   Double.TryParse = True
IsNumeric("12abc") = False  Double.TryParse = False
IsNumeric("") = False  Double.TryParse = False
-- IsNumeric on non-strings
True True False True False
-- TypeName, VarType, VbTypeName
Integer         Integer   Int32
Long            Long      Int64
Double          Double    Double
Single          Single    Single
Decimal         Decimal   Decimal
String          String    String
Char            Char      Char
Boolean         Boolean   Boolean
Date            Date      DateTime
Nothing         Object    (Nothing)
Integer()       8195      Int32[]
List(Of String) Object    List`1
DBNull          Null      DBNull
Byte            Byte      Byte
-- the Is functions
IsArray True, IsNothing True, IsDBNull True, IsReference True/False, IsError True
LBound 0, UBound 2
-- RGB and QBColor return BGR integers
80FF FF
System.Int32 Integer System.Boolean
  • IsNumeric is much more permissive than Double.TryParse: it accepts currency symbols, parentheses for negatives, the &H and &O prefixes and surrounding spaces. It is True for "&HFF", which Double.Parse rejects, and it says nothing about whether the value fits in the target type. It also treats a Boolean and a Char digit as numeric but not a Date. IsNumeric(Nothing) is False. Use TryParse with an explicit culture and NumberStyles.

  • TypeName returns the Visual Basic name (Integer, Date, Integer(), List(Of String)), VarType returns a VariantType (a VB6 code; an array adds 8192, so an Integer() is 8195) and falls back to Object for classes, and VbTypeName/SystemTypeName map between the VB names and the .NET names. GetType(...).Name is the .NET name.

  • IsArray, IsDBNull, IsReference and IsError are one-line tests that read well in old code; TypeOf x Is T and x Is Nothing are the modern forms. IsNothing(x) is the function form of x Is Nothing.

  • LBound and UBound work on any array; LBound is always 0 in .NET. RGB and QBColor return a BGR integer (RGB(255, 128, 0) is &H0080FF), not the ARGB value of Color.FromArgb.

The legacy file I/O in FileSystem

The FileSystem module of Microsoft.VisualBasic (not the FileIO.FileSystem class) is the VB6 file model: you open a file on a file number, then Print, Write, Input, LineInput, FileGet and FilePut on that number. It is not obsolete and it works on .NET, but System.IO is the model for new code (see Files, Directories and Streams). The program exercises all four modes and the file-system helpers; the first line turns CA1416 off because Dir and Rename are annotated as Windows-only:

Option Strict On

#Disable Warning CA1416 ' Dir and Rename are annotated as Windows-only; this program runs on Linux too

Imports System.IO

Module LegacyFileTour
    Sub Main()
        Dim folder As String = Path.Combine(Path.GetTempPath(), "shelf-legacy-io")
        If Directory.Exists(folder) Then Directory.Delete(folder, True)
        MkDir(folder)
        Dim textFile As String = Path.Combine(folder, "books.txt")

        Console.WriteLine("-- report output: Print, PrintLine, TAB and SPC")
        Dim n As Integer = FreeFile()
        FileOpen(n, textFile, OpenMode.Output)
        PrintLine(n, "Title", TAB(12), "Year")
        PrintLine(n, "Dune", TAB(12), 1965)
        Print(n, "no line end, ")
        PrintLine(n, "then one", SPC(3), "x")
        PrintLine(n, "a", "b", True)
        FileClose(n)
        Console.Write(File.ReadAllText(textFile))

        Console.WriteLine("-- data output and input: WriteLine, Input, EOF")
        Dim dataFile As String = Path.Combine(folder, "books.csv")
        n = FreeFile()
        FileOpen(n, dataFile, OpenMode.Output)
        WriteLine(n, "Dune", 1965, True)
        WriteLine(n, "Emma", 1815, False)
        FileClose(n)
        Console.WriteLine("line ends written by WriteLine: LF=" & File.ReadAllText(dataFile).Count(Function(c) c = vbLf).ToString() & " CR=" & File.ReadAllText(dataFile).Count(Function(c) c = vbCr).ToString())
        ' Input recognises CR and CRLF but not a lone LF, so give it a file with Windows line ends
        File.WriteAllText(dataFile, File.ReadAllText(dataFile).Replace(vbLf, vbCrLf))
        n = FreeFile()
        FileOpen(n, dataFile, OpenMode.Input)
        Do Until EOF(n)
            Dim title As String = Nothing, year As Integer, inPrint As Boolean
            Input(n, title)
            Input(n, year)
            Input(n, inPrint)
            Console.WriteLine("read       : " & title & ", " & year.ToString() & ", " & inPrint.ToString())
        Loop
        Console.WriteLine("EOF = " & EOF(n).ToString() & ", LOF = " & LOF(n).ToString() & " bytes")
        FileClose(n)

        Console.WriteLine("-- whole lines: LineInput")
        n = FreeFile()
        FileOpen(n, textFile, OpenMode.Input)
        Dim lines As Integer = 0
        Do Until EOF(n)
            Dim line As String = LineInput(n)
            lines += 1
            If lines = 1 Then Console.WriteLine("first line : '" & line & "'")
        Loop
        Console.WriteLine("lines      : " & lines.ToString())
        FileClose(n)

        Console.WriteLine("-- random access with a fixed-length record")
        Dim recordFile As String = Path.Combine(folder, "books.dat")
        Dim rec As Integer = FreeFile()
        FileOpen(rec, recordFile, OpenMode.Random, OpenAccess.ReadWrite, OpenShare.Default, 4)
        FilePut(rec, 1965, 1)
        FilePut(rec, 1815, 2)
        FilePut(rec, 1922, 3)
        Dim value As Integer
        FileGet(rec, value, 2)
        Console.WriteLine("record 2   : " & value.ToString() & ", records = " & (LOF(rec) \ 4).ToString())
        FileClose(rec)

        Console.WriteLine("-- binary access with Seek")
        Dim bin As Integer = FreeFile()
        FileOpen(bin, Path.Combine(folder, "bytes.bin"), OpenMode.Binary)
        FilePut(bin, CByte(7))
        FilePut(bin, 258S)
        Seek(bin, 1)
        Dim b As Byte, s As Short
        FileGet(bin, b)
        FileGet(bin, s)
        Console.WriteLine("read back  : " & b.ToString() & ", " & s.ToString() & ", position = " & Seek(bin).ToString())
        FileClose(bin)

        Console.WriteLine("-- the file-system helpers")
        FileCopy(textFile, Path.Combine(folder, "copy.txt"))
        Try
            Rename(Path.Combine(folder, "copy.txt"), Path.Combine(folder, "backup.txt"))
        Catch ex As PlatformNotSupportedException
            Console.WriteLine("Rename     : " & ex.GetType().Name & ": " & ex.Message)
            File.Move(Path.Combine(folder, "copy.txt"), Path.Combine(folder, "backup.txt"))
        End Try
        Console.WriteLine("FileLen    : " & FileLen(textFile).ToString() & " bytes")
        Console.WriteLine("GetAttr    : " & GetAttr(textFile).ToString())
        Dim names As New List(Of String)()
        Dim found As String = Dir(Path.Combine(folder, "*.txt"))
        Do While found <> ""
            names.Add(found)
            found = Dir()
        Loop
        names.Sort()
        Console.WriteLine("Dir        : " & String.Join(", ", names))
        Kill(Path.Combine(folder, "backup.txt"))
        Console.WriteLine("after Kill : " & Directory.GetFiles(folder).Length.ToString() & " files")
        Directory.Delete(folder, True)
    End Sub
End Module
-- report output: Print, PrintLine, TAB and SPC
Title      Year
Dune        1965
no line end, then one   x
a             b             True
-- data output and input: WriteLine, Input, EOF
line ends written by WriteLine: LF=2 CR=0
read       : Dune, 1965, True
read       : Emma, 1815, False
EOF = True, LOF = 41 bytes
-- whole lines: LineInput
first line : 'Title      Year'
lines      : 4
-- random access with a fixed-length record
record 2   : 1815, records = 3
-- binary access with Seek
read back  : 7, 258, position = 4
-- the file-system helpers
Rename     : PlatformNotSupportedException: Operation is not supported on this platform.
FileLen    : 93 bytes
GetAttr    : Normal
Dir        : backup.txt, books.txt
after Kill : 4 files
  • Print/PrintLine are for reports: items are separated into 14-character print zones (the a b True line), TAB(n) moves to a column, SPC(n) inserts spaces. Write/WriteLine write data: strings in quotes, booleans as #TRUE#, items separated by commas, which Input reads back.

  • Input recognises CR and CRLF as the end of a line, not a lone LF. WriteLine wrote LF only on Linux (the program counts 2 LF and no CR), and a first version of the program that read it straight back failed with InvalidCastException: Conversion from string ... to type 'Boolean' is not valid. The program therefore rewrites the file with vbCrLf before reading it. LineInput and EOF coped with the LF file.

  • Random-access files use a record length given in FileOpen; FilePut(n, value, recordNumber) and FileGet are one-based in record numbers and LOF is in bytes. The first draft of the example opened the file with record length 8 for 4-byte integers and LOF \ 8 gave 2 records instead of 3, so keep the record length equal to the data.

  • Binary mode uses Seek (one-based position). Fixed-length strings need VBFixedString.

  • Rename threw PlatformNotSupportedException on Linux, so the program falls back to File.Move. Dir with a pattern and then Dir() with no arguments returns the next match, "" at the end; the sort in the program is only to make the output order stable.

  • FileOpen(..., OpenMode.Output) truncates; OpenMode.Append appends; the OpenAccess and OpenShare enumerations give the access and the locking. A forgotten FileClose keeps the file locked until the process exits.

  • The FilePut overload that takes an Object is obsolete (see below); write a single value with FilePutObject or give FilePut a typed variable.

Financial and VBMath

The Financial module has the Excel-style time-value-of-money functions; VBMath has Rnd and Randomize. (Rounding, Decimal and the other numeric topics are on Numbers and Math.)

Option Strict On

Module FinancialMathTour
    Sub Main()
        Console.WriteLine("-- loan and annuity functions (negative = money paid out)")
        Dim payment As Double = Pmt(0.05 / 12, 60, 20000)
        Console.WriteLine("Pmt   : " & payment.ToString("F2"))
        Console.WriteLine("IPmt  : " & IPmt(0.05 / 12, 1, 60, 20000).ToString("F2") & ", PPmt: " & PPmt(0.05 / 12, 1, 60, 20000).ToString("F2"))
        Console.WriteLine("FV    : " & FV(0.04 / 12, 120, -100).ToString("F2"))
        Console.WriteLine("PV    : " & PV(0.05 / 12, 60, -377.42).ToString("F2"))
        Console.WriteLine("NPer  : " & NPer(0.05 / 12, -377.42, 20000).ToString("F1"))
        Console.WriteLine("Rate  : " & (Rate(60, -377.42, 20000) * 12).ToString("P2"))
        Console.WriteLine("-- cash-flow functions")
        Dim flows As Double() = {-1000, 300, 400, 500}
        Console.WriteLine("NPV   : " & NPV(0.1, flows).ToString("F2") & "  (the first value is discounted too)")
        Console.WriteLine("IRR   : " & IRR(flows).ToString("P2"))
        Console.WriteLine("MIRR  : " & MIRR(flows, 0.08, 0.1).ToString("P2"))
        Console.WriteLine("-- depreciation")
        Console.WriteLine("SLN   : " & SLN(10000, 1000, 5).ToString("F2"))
        Console.WriteLine("SYD   : " & SYD(10000, 1000, 5, 1).ToString("F2"))
        Console.WriteLine("DDB   : " & DDB(10000, 1000, 5, 1).ToString("F2") & ", year 2: " & DDB(10000, 1000, 5, 2).ToString("F2"))

        Console.WriteLine("-- VBMath: Rnd and Randomize")
        Rnd(-1)
        Randomize(42)
        Dim a As Single = Rnd()
        Dim b As Single = Rnd()
        Rnd(-1)
        Randomize(42)
        Console.WriteLine("same seed gives the same sequence: " & (a = Rnd() AndAlso b = Rnd()).ToString())
        Console.WriteLine("range 0 <= Rnd() < 1: " & (a >= 0.0F AndAlso a < 1.0F).ToString())
        Console.WriteLine("Rnd(0) repeats the last value: " & (Rnd(0) = Rnd(0)).ToString())
        Dim dice As Integer = CInt(Int(Rnd() * 6)) + 1
        Console.WriteLine("dice between 1 and 6: " & (dice >= 1 AndAlso dice <= 6).ToString())
    End Sub
End Module
-- loan and annuity functions (negative = money paid out)
Pmt   : -377.42
IPmt  : -83.33, PPmt: -294.09
FV    : 14724.98
PV    : 19999.75
NPer  : 60.0
Rate  : 5.00 %
-- cash-flow functions
NPV   : -19.12  (the first value is discounted too)
IRR   : 8.90 %
MIRR  : 9.22 %
-- depreciation
SLN   : 1800.00
SYD   : 3000.00
DDB   : 4000.00, year 2: 2400.00
-- VBMath: Rnd and Randomize
same seed gives the same sequence: True
range 0 <= Rnd() < 1: True
Rnd(0) repeats the last value: True
dice between 1 and 6: True
  • The sign convention is the Excel one: money you pay is negative (Pmt is -377.42 for a 20,000 loan), money you receive is positive. Mixing the signs gives silently wrong answers or an exception from IRR.

  • The rate and the number of periods must use the same unit (0.05 / 12 with 60 months). The functions work in Double; do not use them to post amounts to a ledger, use Decimal and round once.

  • NPV discounts every value from the first period, so the initial investment (period 0) is usually added outside the call. IRR starts from a guess and can fail to converge.

  • DDB, SLN and SYD are three depreciation methods; DDB takes an optional factor (2 means double-declining).

  • Rnd returns a Single in [0, 1). The recipe for repeatable numbers is Rnd(-1) followed by Randomize(seed), as in the program; without Randomize the sequence is the same in every process: a separate check printed 0.7055475 for the first Rnd() in two different runs, so a program that forgets Randomize repeats itself. Rnd(0) returns the last value again. Rnd is not suitable for anything that must be unpredictable or fair: use Random.Shared or RandomNumberGenerator.

ErrObject

Err is a global ErrObject that VB6 used for error handling. In structured code it is still there: after an exception is caught, Err describes it. It also provides Err.Raise, which throws.

Option Strict On

Module ErrTour
    Sub Main()
        Console.WriteLine("-- Err is a global object that mirrors the last exception")
        Try
            Dim zero As Integer = 0
            Console.WriteLine(10 \ zero)
        Catch ex As DivideByZeroException
            Console.WriteLine("Err.Number      : " & Err.Number.ToString())
            Console.WriteLine("Err.Description : " & Err.Description)
            Console.WriteLine("Err.Source      : " & Err.Source)
            Console.WriteLine("GetException is the same object: " & Object.ReferenceEquals(Err.GetException(), ex).ToString())
            Err.Clear()
            Console.WriteLine("after Clear     : " & Err.Number.ToString() & " '" & Err.Description & "'")
        End Try

        Console.WriteLine("-- Err.Raise throws an exception that Try can catch")
        Try
            Err.Raise(vbObjectError + 1000, "ShelfApp", "The shelf is full")
        Catch ex As Exception
            Console.WriteLine(ex.GetType().Name & ": " & ex.Message & " (Err.Number = " & (Err.Number - vbObjectError).ToString() & " + vbObjectError, Source = " & Err.Source & ")")
        End Try
        Try
            Err.Raise(5)
        Catch ex As Exception
            Console.WriteLine(ex.GetType().Name & ": " & ex.Message)
        End Try
        Try
            Err.Raise(53)
        Catch ex As Exception
            Console.WriteLine(ex.GetType().Name & ": " & ex.Message)
        End Try

        Console.WriteLine("-- Err.Number for other exceptions")
        Dim samples As New List(Of Exception) From {
            New InvalidCastException(), New OverflowException(), New IndexOutOfRangeException(), New NullReferenceException(),
            New IO.FileNotFoundException(), New FormatException(), New ArgumentException(), New OutOfMemoryException()
        }
        For Each ex As Exception In samples
            Try
                Throw ex
            Catch caught As Exception
                Console.WriteLine(caught.GetType().Name.PadRight(26) & Err.Number.ToString().PadLeft(6) & "  " & Err.Description)
            End Try
        Next
    End Sub
End Module
-- Err is a global object that mirrors the last exception
Err.Number      : 11
Err.Description : Attempted to divide by zero.
Err.Source      : System.Private.CoreLib
GetException is the same object: True
after Clear     : 0 ''
-- Err.Raise throws an exception that Try can catch
Exception: The shelf is full (Err.Number = 1000 + vbObjectError, Source = ShelfApp)
ArgumentException: Procedure call or argument is not valid.
FileNotFoundException: File not found.
-- Err.Number for other exceptions
InvalidCastException          13  Specified cast is not valid.
OverflowException              6  Arithmetic operation resulted in an overflow.
IndexOutOfRangeException       9  Index was outside the bounds of the array.
NullReferenceException        91  Object reference not set to an instance of an object.
FileNotFoundException         53  Unable to find the specified file.
FormatException               13  One of the identified items was in an invalid format.
ArgumentException              5  Value does not fall within the expected range.
OutOfMemoryException           7  Insufficient memory to continue the execution of the program.

Source: ErrObject class

  • Err.Number maps .NET exceptions to the classic VB6 numbers (11 division by zero, 13 type mismatch, 6 overflow, 9 subscript out of range, 91 object variable not set, 53 file not found, 5 invalid procedure call, 7 out of memory). FormatException and InvalidCastException both map to 13.

  • Err.GetException() returns the real exception object, so Err adds nothing that Catch ex does not give; use the exception.

  • Err.Raise(number, source, description) throws a plain Exception for a custom number (the example uses vbObjectError + 1000) and a typed exception for known numbers (Err.Raise(5) throws ArgumentException, Err.Raise(53) throws FileNotFoundException). A custom exception class is clearer.

  • The unstructured error-handling statements that fill Err are covered on Unstructured Error Handling: On Error; structured exceptions are on Structured Exception Handling.

Collection

The VB6 Collection stores Object items, is one-based, and can be addressed by position or by a case-insensitive string key; Add can place an item Before or After another.

Option Strict On

Module CollectionTour
    Sub Main()
        Dim shelf As New Collection()
        shelf.Add("Dune", "dune")
        shelf.Add("Emma", "emma")
        shelf.Add("Ulysses")
        shelf.Add("Beloved", "beloved", Before:=1)
        shelf.Add("Ivanhoe", "ivanhoe", After:=2)

        Console.WriteLine("-- one-based, ordered by Before and After")
        For i As Integer = 1 To shelf.Count
            Console.Write(i.ToString() & "=" & CStr(shelf(i)) & " ")
        Next
        Console.WriteLine()
        Console.WriteLine("-- keys are case-insensitive")
        Console.WriteLine(CStr(shelf("DUNE")) & ", Contains: " & shelf.Contains("emma").ToString() & "/" & shelf.Contains("zzz").ToString())
        Console.WriteLine("-- items are Object, For Each yields Object")
        For Each item As Object In shelf
            Console.Write(item.ToString() & ";")
        Next
        Console.WriteLine()
        Console.WriteLine("-- errors")
        Try
            shelf.Add("Duplicate", "dune")
        Catch ex As ArgumentException
            Console.WriteLine("add duplicate key  : " & ex.GetType().Name & ": " & ex.Message)
        End Try
        Try
            Console.WriteLine(shelf(0))
        Catch ex As IndexOutOfRangeException
            Console.WriteLine("index 0            : " & ex.GetType().Name & ": " & ex.Message)
        End Try
        Try
            Console.WriteLine(shelf("zzz"))
        Catch ex As ArgumentException
            Console.WriteLine("missing key        : " & ex.GetType().Name & ": " & ex.Message)
        End Try
        shelf.Remove("emma")
        shelf.Remove(1)
        Console.WriteLine("after Remove       : " & shelf.Count.ToString())

        Console.WriteLine("-- the generic replacements")
        Dim titles As New List(Of String) From {"Dune", "Emma"}
        Dim byKey As New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase) From {{"dune", "Dune"}, {"emma", "Emma"}}
        Console.WriteLine(titles(0) & " " & byKey("EMMA") & " " & byKey.ContainsKey("Dune").ToString())
    End Sub
End Module
-- one-based, ordered by Before and After
1=Beloved 2=Dune 3=Ivanhoe 4=Emma 5=Ulysses
-- keys are case-insensitive
Dune, Contains: True/False
-- items are Object, For Each yields Object
Beloved;Dune;Ivanhoe;Emma;Ulysses;
-- errors
add duplicate key  : ArgumentException: Add failed. Duplicate key value supplied.
index 0            : IndexOutOfRangeException: Collection index must be in the range 1 to the size of the collection.
missing key        : ArgumentException: Argument 'Index' is not a valid value. (Parameter 'Key')
after Remove       : 3
-- the generic replacements
Dune Emma True

It is not a List and it is not a Dictionary: the items are untyped (casts everywhere), indexing is one-based, the same object answers to a number and to a key, and an invalid index throws IndexOutOfRangeException while a missing key throws ArgumentException. Use List(Of T) for sequences and Dictionary(Of TKey, TValue) for keyed access (see Collections); keep Collection for code that has to match old behaviour, and in the compatibility shims of a migration (see Migrating Legacy Visual Basic).

Constants and ControlChars

Constants holds vbCrLf, vbTab, vbTrue, vbObjectError and a copy of the members of the VB enumerations (vbYes, vbBinaryCompare, vbMonday, vbString …​). ControlChars has the same characters as Char or String fields.

Option Strict On

Module ConstantsTour
    Private Function Codes(text As String) As String
        Return String.Join(" ", text.Select(Function(c) AscW(c).ToString()))
    End Function

    Sub Main()
        Console.WriteLine("-- line and character constants (character codes)")
        Console.WriteLine("vbCrLf            : " & Codes(vbCrLf))
        Console.WriteLine("vbCr, vbLf        : " & Codes(vbCr) & ", " & Codes(vbLf))
        Console.WriteLine("vbTab, vbBack     : " & Codes(vbTab) & ", " & Codes(vbBack))
        Console.WriteLine("vbFormFeed, vbVT  : " & Codes(vbFormFeed) & ", " & Codes(vbVerticalTab))
        Console.WriteLine("vbNullChar        : " & Codes(vbNullChar))
        Console.WriteLine("Environment.NewLine here : " & Codes(Environment.NewLine))
        Console.WriteLine("ControlChars.NewLine here: " & Codes(ControlChars.NewLine))
        Console.WriteLine("ControlChars.CrLf        : " & Codes(ControlChars.CrLf))
        Console.WriteLine("ControlChars.Quote       : " & AscW(ControlChars.Quote).ToString() & " (a Char)")
        Console.WriteLine("-- vbNullString is Nothing, not an empty string")
        Console.WriteLine((vbNullString Is Nothing).ToString() & " " & (vbNullString = "").ToString() & " " & Len(vbNullString).ToString())
        Console.WriteLine("-- numeric constants")
        Console.WriteLine("vbTrue " & vbTrue.ToString() & ", vbFalse " & vbFalse.ToString() & ", vbObjectError " & vbObjectError.ToString())
        Console.WriteLine("vbBinaryCompare " & CInt(vbBinaryCompare).ToString() & ", vbTextCompare " & CInt(vbTextCompare).ToString())
        Console.WriteLine("vbSunday " & CInt(vbSunday).ToString() & ", vbMonday " & CInt(vbMonday).ToString() & ", vbUseSystemDayOfWeek " & CInt(vbUseSystemDayOfWeek).ToString())
        Console.WriteLine("-- MsgBox style and result constants combine as flags")
        Dim style As MsgBoxStyle = MsgBoxStyle.YesNo Or MsgBoxStyle.Question Or MsgBoxStyle.DefaultButton2
        Console.WriteLine("YesNo Or Question Or DefaultButton2 = " & CInt(style).ToString() & " (" & style.ToString() & ")")
        Console.WriteLine("vbYes " & CInt(vbYes).ToString() & ", vbNo " & CInt(vbNo).ToString() & ", vbOK " & CInt(vbOK).ToString() & ", vbCancel " & CInt(vbCancel).ToString())
        Console.WriteLine("-- VarType values")
        Console.WriteLine("vbInteger " & CInt(vbInteger).ToString() & ", vbString " & CInt(vbString).ToString() & ", vbArray " & CInt(vbArray).ToString() & ", vbObject " & CInt(vbObject).ToString())
    End Sub
End Module
-- line and character constants (character codes)
vbCrLf            : 13 10
vbCr, vbLf        : 13, 10
vbTab, vbBack     : 9, 8
vbFormFeed, vbVT  : 12, 11
vbNullChar        : 0
Environment.NewLine here : 10
ControlChars.NewLine here: 13 10
ControlChars.CrLf        : 13 10
ControlChars.Quote       : 34 (a Char)
-- vbNullString is Nothing, not an empty string
True True 0
-- numeric constants
vbTrue True, vbFalse False, vbObjectError -2147221504
vbBinaryCompare 0, vbTextCompare 1
vbSunday 1, vbMonday 2, vbUseSystemDayOfWeek 0
-- MsgBox style and result constants combine as flags
YesNo Or Question Or DefaultButton2 = 292 (YesNo, Question, DefaultButton2)
vbYes 6, vbNo 7, vbOK 1, vbCancel 2
-- VarType values
vbInteger 3, vbString 8, vbArray 8192, vbObject 9
  • vbCrLf, vbCr, vbLf, vbTab and ControlChars.CrLf are fixed characters. ControlChars.NewLine is also fixed CR LF (printed as 13 10) even though its name suggests the platform’s line end: on Linux Environment.NewLine is LF (10) only. Use Environment.NewLine for text that should follow the platform and vbCrLf where the protocol or file format demands CR LF.

  • vbNullString is Nothing yet compares equal to "", and has Len 0; vbNullChar is the character 0 (used with API calls).

  • vbTrue and vbFalse are TriState values and print as True/False; the numeric value of True is -1 in Visual Basic (CInt(True)); C# has no implicit conversion from bool to an integer.

  • The result and style constants (vbYes, vbYesNo, vbQuestion, vbDefaultButton2) are MsgBoxStyle/MsgBoxResult values that combine with Or. They only make sense with MsgBox.

  • Obsolete: vbNewLine is marked obsolete: the compiler reports BC40000 with the text shown next. The FilePut overload with an Object parameter is obsolete too (the message recommends FilePutObject); passing an Object variable to FilePut on this SDK gave error BC30518: Overload resolution failed because no accessible 'FilePut' can be called with these arguments.

The program below (built with TreatWarningsAsErrors set to false, so the warnings are only warnings) triggers the obsolete constant and two Windows-only annotations:

Option Strict On

Module Program
    Sub Main()
        Dim line As String = "Dune" & vbNewLine
        Dim n As Integer = FreeFile()
        Dim value As Object = 42
        FileOpen(n, "/tmp/x.dat", OpenMode.Binary)
        FilePutObject(n, value)
        FileClose(n)
        Console.WriteLine(StrConv("dune", VbStrConv.ProperCase))
        Beep()
        Console.WriteLine(line.Length.ToString())
    End Sub
End Module
Program.vb(5,39): warning BC40000: 'Public Const vbNewLine As String' is obsolete: 'vbNewLine has been deprecated. For a carriage return and line feed, use vbCrLf. For the current platform's newline, use System.Environment.NewLine.'.
Program.vb(11,27): warning CA1416: This call site is reachable on all platforms. 'Public Overloads Function StrConv(str As String, Conversion As VbStrConv, [LocaleID As Integer = 0]) As String' is only supported on: 'windows'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)
Program.vb(12,9): warning CA1416: This call site is reachable on all platforms. 'Public Overloads Sub Beep()' is only supported on: 'windows'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)

Enumerations of the library

Enumeration Used by

CompareMethod (Binary, Text)

the Compare argument of InStr, Replace, Split, StrComp, Filter

DateInterval, FirstDayOfWeek, FirstWeekOfYear, DateFormat

DateAdd, DateDiff, DatePart, Weekday, FormatDateTime

MsgBoxStyle, MsgBoxResult, AppWinStyle

MsgBox, Shell

OpenMode, OpenAccess, OpenShare, FileAttribute

FileOpen, GetAttr, SetAttr, Dir

TriState

the formatting functions, vbTrue

VariantType, CallType, VbStrConv, DueDate

VarType, CallByName, StrConv, the depreciation and annuity functions

CompilerServices: code you do not write

The 29 public types of Microsoft.VisualBasic.CompilerServices are called by the compiler: Conversions, Operators, LikeOperator, NewLateBinding, ObjectFlowControl, StaticLocalInitFlag, Versioned, Utils, ProjectData, StandardModuleAttribute, OptionCompareAttribute and the ...Type helper classes. They are public because the generated code has to call them from your assembly. They are hidden from IntelliSense and not part of the supported surface, but nothing stops you calling them. Reading the IL of five methods shows which helper each construct becomes:

Option Strict On

Imports System.ComponentModel
Imports System.Reflection
Imports System.Reflection.Emit
Imports System.Runtime.CompilerServices

' Four small methods whose compiled bodies we will inspect
Public NotInheritable Class Probe
    Public Shared Function Matches(text As String, pattern As String) As Boolean
        Return text Like pattern
    End Function

    Public Shared Function Same(a As String, b As String) As Boolean
        Return a = b
    End Function

    Public Shared Function ToInt(value As Object) As Integer
        Return CInt(value)
    End Function

    Public Shared Function Shorten(text As String) As String
        Return Left(text, 3) & Trim(text)
    End Function

    Public Shared Function Describe(value As Object) As String
        Return TypeName(value) & Len(CStr(value)).ToString()
    End Function
End Class

Module CompilerServicesTour
    ' Lists the methods called by a method body by scanning its IL for call opcodes
    Private Function CalledMethods(m As MethodInfo) As List(Of String)
        Dim result As New List(Of String)()
        Dim il As Byte() = m.GetMethodBody().GetILAsByteArray()
        For i As Integer = 0 To il.Length - 5
            If il(i) = &H28 OrElse il(i) = &H6F Then
                Dim token As Integer = BitConverter.ToInt32(il, i + 1)
                Dim table As Integer = (token >> 24) And &HFF
                If table = &H6 OrElse table = &HA OrElse table = &H2B Then
                    Try
                        Dim target As MethodBase = m.Module.ResolveMethod(token)
                        result.Add(target.DeclaringType.Name & "." & target.Name)
                        i += 4
                    Catch ex As ArgumentException
                    End Try
                End If
            End If
        Next
        Return result
    End Function

    Sub Main()
        For Each name As String In {"Matches", "Same", "ToInt", "Shorten", "Describe"}
            Dim m As MethodInfo = GetType(Probe).GetMethod(name)
            Console.WriteLine(name.PadRight(10) & String.Join(", ", CalledMethods(m)))
        Next

        Console.WriteLine("-- who may call the helper classes")
        Dim asm As Assembly = GetType(Microsoft.VisualBasic.CompilerServices.Conversions).Assembly
        Dim hidden As Integer = 0, total As Integer = 0
        For Each t As Type In asm.GetExportedTypes()
            If t.Namespace = "Microsoft.VisualBasic.CompilerServices" Then
                total += 1
                Dim eb = t.GetCustomAttribute(Of EditorBrowsableAttribute)()
                If eb IsNot Nothing AndAlso eb.State = EditorBrowsableState.Never Then hidden += 1
            End If
        Next
        Console.WriteLine(hidden.ToString() & " of " & total.ToString() & " public types in CompilerServices are hidden from IntelliSense")
        Console.WriteLine("Conversions.ToInteger(""12"") still works: " & Microsoft.VisualBasic.CompilerServices.Conversions.ToInteger("12").ToString())
    End Sub
End Module
Matches   LikeOperator.LikeString
Same      Operators.CompareString
ToInt     Conversions.ToInteger
Shorten   Strings.Left, Strings.Trim, String.Concat
Describe  RuntimeHelpers.GetObjectValue, Versioned.TypeName, Conversions.ToString, Strings.Len, Int32.ToString, String.Concat
-- who may call the helper classes
29 of 29 public types in CompilerServices are hidden from IntelliSense
Conversions.ToInteger("12") still works: 12
  • Like is LikeOperator.LikeString, and = on strings is Operators.CompareString (which is why Option Compare Text changes it); CInt of an Object is Conversions.ToInteger.

  • Left, Trim and Len are ordinary calls into the Strings module, so they cost a method call and an argument check where Substring costs a method call.

  • Versioned.TypeName is what the TypeName function calls, and RuntimeHelpers.GetObjectValue appears around an Object argument to preserve value-type semantics.

  • The program scans for call/callvirt opcodes and resolves their tokens; it is a demonstration, not a disassembler. Use ildasm, ILSpy or SharpLab for exact IL.

  • Do not call these helpers yourself: their signatures and behaviour can change without notice, NewLateBinding is why late binding is slow, and the Fallback... members are marked obsolete.

Microsoft.VisualBasic.Compatibility.VB6

On .NET Framework a separate assembly, Microsoft.VisualBasic.Compatibility, offered the VB6 migration helpers (Microsoft.VisualBasic.Compatibility.VB6: control arrays, font helpers, Support functions used by the Upgrade Wizard). It is a .NET Framework component only.

Option Strict On

Imports System.IO
Imports System.Reflection

Module CompatTour
    Sub Main()
        Dim core As Assembly = GetType(Microsoft.VisualBasic.Strings).Assembly
        Dim namespaces As List(Of String) = core.GetExportedTypes().Select(Function(t) If(t.Namespace, "")).Distinct().OrderBy(Function(n) n).ToList()
        Console.WriteLine("Namespaces in " & core.GetName().Name & ":")
        For Each ns As String In namespaces
            Console.WriteLine("  " & ns)
        Next
        Console.WriteLine("Any namespace with 'Compatibility': " & namespaces.Any(Function(n) n.Contains("Compatibility")).ToString())
        Console.WriteLine("Type found by name: " & (Type.GetType("Microsoft.VisualBasic.Compatibility.VB6.Support, Microsoft.VisualBasic.Compatibility") IsNot Nothing).ToString())
        Try
            Assembly.Load("Microsoft.VisualBasic.Compatibility")
        Catch ex As FileNotFoundException
            Console.WriteLine(ex.GetType().Name & ": " & ex.Message.TrimEnd())
        End Try
        Console.WriteLine("Runtime assemblies named Microsoft.VisualBasic*:")
        For Each f As String In Directory.GetFiles(Path.GetDirectoryName(GetType(Object).Assembly.Location), "Microsoft.VisualBasic*.dll").OrderBy(Function(x) x)
            Console.WriteLine("  " & Path.GetFileName(f))
        Next
    End Sub
End Module
Namespaces in Microsoft.VisualBasic.Core:
  Microsoft.VisualBasic
  Microsoft.VisualBasic.CompilerServices
  Microsoft.VisualBasic.FileIO
Any namespace with 'Compatibility': False
Type found by name: False
FileNotFoundException: Could not load file or assembly 'Microsoft.VisualBasic.Compatibility, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
Runtime assemblies named Microsoft.VisualBasic*:
  Microsoft.VisualBasic.Core.dll
  Microsoft.VisualBasic.dll

The runtime that ships with .NET has no such namespace or assembly. Code that uses it must be rewritten to ordinary Windows Forms or WPF code; see Migrating Legacy Visual Basic and The VB Family: VB6, VBA and VBScript.

When to prefer the BCL

Runtime library BCL Reason to choose the BCL

Left, Mid, Right, InStr

Substring, IndexOf, Contains, ranges

zero-based like the rest of .NET, culture and comparison explicit; but the VB functions never throw for a too-long length

UCase, LCase

ToUpperInvariant, ToLowerInvariant

the culture is explicit

Format, FormatNumber

interpolation with format strings, ToString(format, culture)

culture and format are visible

CStr, Str

ToString

no leading space, culture explicit

Val, IsNumeric

TryParse with NumberStyles and culture

no guessing

IsNothing, TypeName

Is Nothing, GetType().Name

reads the same in every .NET language

DateAdd, DateDiff

DateTime.AddMonths, subtraction to TimeSpan

no boundary-count surprise

Now, Today

DateTime.Now, TimeProvider

testable clock

FileOpen/Print/Input

StreamReader, StreamWriter, File

encodings, async, Using

Collection

List(Of T), Dictionary(Of ...)

typed and zero-based

Err, Err.Raise

exceptions

typed, stack traces

Shell, Environ, Command

Process.Start, Environment, args

cross-platform, arguments preserved

MsgBox, InputBox

MessageBox.Show, own dialog

no hidden Windows Forms dependency in the call

Rnd, Randomize

Random, RandomNumberGenerator

quality, thread safety

Choose, Interaction.Switch

Select Case, If

only the needed branch is evaluated

Pmt, NPV, IRR etc.

none in the BCL

keep the VB functions, in Decimal-aware wrappers if money is involved

The VB functions remain good when you port VB6 or VBA logic unchanged, when you need the VB-specific behaviour on purpose (tolerance of Nothing, clipping), or when brevity matters in a throw-away script.

Differences from C#

  • C# has no equivalent keywords: every function above is called as Strings.Left(...), Interaction.Choose(...), Financial.Pmt(...) after adding using Microsoft.VisualBasic; (the reference is already available in .NET projects). Calling FileSystem.FileOpen from C# works and is used when reading legacy files.

  • Type conversion keywords (CInt, CType) are language features in Visual Basic and have no C# form; Convert.ToInt32 and casts differ in rounding and in the way strings are parsed.

  • vbCrLf and the other constants have no C# counterpart; C# uses the escape sequences "\r\n".

  • C# compares strings with == ordinally, while Visual Basic’s = follows Option Compare; the library functions that have a Compare argument follow it as well.

  • See Strings and Text (C#) and C# and .NET for the other side.

Where the books are outdated

Book habit Today

vbNewLine in every example

obsolete (BC40000); use vbCrLf or Environment.NewLine

FilePut with an Object

obsolete; use FilePutObject

MsgBox and InputBox as the I/O of a console exercise

throw PlatformNotSupportedException; use Console

Shell("notepad")

throws on Linux; Process.Start

GetSetting/SaveSetting for preferences

Windows registry only; use JSON or My.Settings

Microsoft.VisualBasic.Compatibility.VB6 from the Upgrade Wizard

not available on .NET

DoEvents and Sleep-style loops

Await Task.Delay; see Async and Await

Treating Rnd as random

predictable; use Random.Shared

unstructured error handling plus Err as the main idiom

Try/Catch

Diagnostics reference

Diagnostic Meaning

BC40000

use of an obsolete member (vbNewLine, the FilePut overload for Object)

BC30109

a class name used as an expression, for example Switch(...) resolving to System.Diagnostics.Switch

BC30518

overload resolution failed, for example FilePut with an Object argument

BC30057

too many arguments to a library function

CA1416

the call is annotated as Windows-only and is reachable on other platforms (an error under TreatWarningsAsErrors)

Pitfalls checklist

  • Positions in the VB string functions are one-based; in the BCL they are zero-based.

  • Trim removes spaces only; Replace with a start argument drops the prefix; Str adds a leading space; Val and IsNumeric accept formats that TryParse rejects.

  • Choose and Interaction.Switch evaluate all arguments; refer to Interaction.Switch explicitly.

  • DateSerial and DateAdd overflow quietly; DateDiff counts boundaries.

  • ControlChars.NewLine is CR LF on every platform; Environment.NewLine follows the platform.

  • The legacy Input needs CR or CR LF line ends.

  • A Windows-only annotation (CA1416) is a build error under TreatWarningsAsErrors; guard with OperatingSystem.IsWindows() or avoid the call.

  • Do not use Rnd, IsNumeric or CallByName where unpredictability, strictness or trimming matter.

  • Do not call Microsoft.VisualBasic.CompilerServices directly.