This page lists some solutions to common MicroStation VBA (MVBA) and VB problems. Few of these tips are original: some are repeated from Microsoft VB or VBA documentation, and some are paraphrased from the MVBA documentation. Unless declared explicitly, the content applies equally to VB or MVBA. Tips are published as examples and are not necessarily working code.

Be Communities

A good place to post questions about MicroStation VBA is the Bentley Communities MicroStation Programming Forum.

Contents

MicroStation VBA Tips Index
Where is the MicroStation VBA Help File? How Windows finds a DLL
Bentley ColorPicker and ColorChooser Does a Cell Exist?
How do I change a MicroStation setting? Is a Database Connection Active?
Design File History Design File Information
Recalculate an element's range Formatting Numbers
Test whether a File Exists Finding a File
Calculating & Formatting Dates Maximum vertex count in a shape or line
Objects & User-Defined Types Insist on Variable Declaration
Variable Initialisation Procedure Calling Convention
Pointers in VB Quoted Strings and File Names
Recording a VBA Macro Enable the scroll wheel in the VBA IDE
Windows Prompts for Installation CD Divide a text string into words
Convert an element to a Line String Run a VBA macro at Startup
Debugging Assistance Set the caption of MicroStation's main window

MicroStation VBA Help

VBA Help

Q Where is the help file for MicroStation VBA?

A  The MicroStation VBA help file is MicroStationVBA.chm.

The .chm extension tells you that it's a compiled HTML help file. MicroStationVBA.chm is installed in the same folder as the MicroStation executable (MicroStation.exe). In other words, if you can find MicroStation, you can find VBA help.

The default location of MicroStation (and VBA help) varies according to the installation. Here are a couple of locations that you may find …

Product Operating System Location
MicroStation 64-bit C:\Program Files\Bentley\MicroStation
CONNECT C:\Program Files\Bentley\MicroStation CONNECT Edition Update 2

MicroStation VBA help is comprehensive. It documents every object in detail, and provides plenty of examples …

VBA Help Examples

Design File Information

Q How do I get some basic information about the current file that I'm working with?

Design File Property: File Name

A  A ModelReference doesn't have a file name because it's one of many models that live inside a DGN file, which in VBA is a DesignFile object. However, that DesignFile has a file name and path. So the solution is to find the ModelReference's parent DesignFile, which is straightforward, and ask its name …

' ---------------------------------------------------------------------
'   GetDgnFileName
'   Returns:    String containing the file path of the given model
' ---------------------------------------------------------------------
Public Function GetDgnFileName(ByVal modelRef As ModelReference) As String
    GetDgnFileName = vbNullString
    On Error GoTo err_GetDgnFileName
    GetDgnFileName = modelRef.DesignFile.FullName
    Exit Function

err_GetDgnFileName:
    MsgBox "Error no. " & CStr(Err.Number) & ": " & Err.Description & vbNewLine & _
    "Caused by " & Err.Source, vbOKOnly Or vbExclamation, "Get DGN File Name Error"
End Function

Here's a test subroutine that gets the active model's file path …


Public Sub TestDgnFileName()
    Dim path                                As String
    path = GetDgnFileName(ActiveModelReference)
    Debug.Print "DGN File Path: " & path
End Sub

Design File Dimension

A  File Dimension is one of those mysterious TCB things: tcb->ndices that has survived, for no apparent reason, in MicroStation V8. Here's more information about the TCB, and here's an example procedure that reveals the file dimension …

' ---------------------------------------------------------------------
'   IsThreeD
'   Returns:    True if the active file is 3D, 2D otherwise
' ---------------------------------------------------------------------
Public Function IsThreeD() As Boolean
    Select Case CLng(GetCExpressionValue("tcb->ndices"))
    Case 3
        IsThreeD = True
        Debug.Print "File is 3D"
    Case 2
        Debug.Print "File is 2D"
        IsThreeD = False
    Case Else
        MsgBox "DGN is neither 2D nor 3D!", vbCritical Or vbOKOnly, "Invalid File Dimension"
    End Select
End Function

Model Dimension

However, with MicroStation V8, File Dimension doesn't mean anything. A DGN file contains one or more models (or cells), each of which may be 2D or 3D. Suppose, for example, that you have a cell library containing three models, one 3D and two 2D. Is that cell library a 2D library or a 3D library?

You can get the dimension of a model like this …

  Debug.Print "Model is " & Iif (oModel.Is3d, "3D", "2D")

In older versions of MicroStation VBA that property of a model was not available, and we had to use an MDL call to get that information …

Declare PtrSafe Function mdlModelRef_is3D Lib "stdmdlbltin.dll" ( ByVal modelRef As Long ) As Long

Public Function IsModel3d (ByVal oModelRef As ModelReference) As Boolean
  If (0 <> mdlModelRef_is3D (oModelRef.MdlModelRefP)) Then
    IsModel3d = True
  Else
    IsModel3d = False
  End If
End Function

Design File History

A  As of MicroStation XM, VBA contains no objects concerned with DGN file history. But there are ways around that limitation: here are a couple of suggestions …

Design File History: MDL Solution

One approach is to use the MDL functions declared below.

If all you want to know is whether history is enabled or not, here's a simple VBA function HistoryExists that wraps the MDL functions. It returns a Boolean value that indicates the state of history.

' ---------------------------------------------------------------------
'   MDL function declarations
' ---------------------------------------------------------------------
Declare PtrSafe Function mdlHistory_exists Lib "stdmdlbltin.dll" (ByVal pFile As Long) As Long
Declare PtrSafe Function mdlModelRef_getDgnFile Lib "stdmdlbltin.dll" (ByVal modelRef As Long) As Long
Declare PtrSafe Function mdlModelRef_getActive Lib "stdmdlbltin.dll" () As Long
' ---------------------------------------------------------------------
Public Function HistoryExists() As Boolean
    HistoryExists = False
    Dim exists                              As Long
    exists = mdlHistory_exists(mdlModelRef_getDgnFile(mdlModelRef_getActive()))
    HistoryExists = exists <> 0
End Function

Design File History: Property Handler Solution

Yongan Fu, of Bentley Systems, suggested using a Property Handler …

Sub TestDesignHistoryOn()
	Dim oPH As PropertyHandler
	Dim revCnt As Integer
	Set oPH = CreatePropertyHandler(ActiveDesignFile)
	oPH.SelectByAccessString "DesignRevisionCount"
	revCnt = oPH.GetValue
	If revCnt = 0 Then
		MsgBox "Design History is OFF"
	Else
		MsgBox "Design History is ON"
	End If
End Sub

Back to contents

Bentley Systems ColorPicker and ColorChooser Controls

Q How can I include a ColorPicker or ColorChooser control in my UserForm?

A Microsoft VBA knows nothing about the application it's used with. The controls available to you as a UserForm designer are those provided with VBA and any that may be provided in control libraries.

Plain old VBA is generic: it doesn't know about CAD and therefore doesn't provide tools for browsing a colour palette or picking a colour from a palette.

In the past, Bentley have provided two controls for colour manipulation: ColorPicker and ColorChooser. However, those controls were unsupported and are no longer available.

As an alternative, look at this article about Using a Microsoft Common Control Color Dialog …

Microsoft: Color Picker

Back to contents

Is a Database Connection Active?

Q How can I tell if MicroStation has an active database connection?

A Use the MDL function mdlDB_isActive(). This example by Jerry Walter at Bentley Systems shows you how …

Declare PtrSafe Function mdlDB_isActive Lib "stdmdlbltin.dll" () As Long
' ---------------------------------------------------------------------
Sub isDbAttachedTest() If IsDbAttached() Then MsgBox "Database is attached", vbOKOnly, "Database" Else MsgBox "Database is not attached", vbOKOnly, "Database" End If End Sub
' ---------------------------------------------------------------------
Function IsDbAttached() As Boolean IsDbAttached = CBool(mdlDB_isActive()) End Function

Back to contents

Running a VBA Macro at Startup

Q How do I run a VBA macro at startup

A You can run a VBA macro as MicroStation starts up, or each time you open a design file.

The answer is at the MicroStation Oracle: Ask Inga.


Back to contents

Procedure Calling Convention

Q When should I use parentheses in a procedure call? When should I not use parentheses in a procedure call?

A A procedure in VB and VBA is either a Function or a Subroutine. A Function returns a value, a Subroutine returns nothing. When a procedure accepts one or more parameters, you pass a variable or value to the procedure.

Subroutines

Suppose we have a simple Subroutine that modifies a variable. This example multiplies the passed variable by a passed value …

Sub Multiply (ByRef change As Double, ByVal value As Double)
  change = change * value
End Sub

You might call the Subroutine like this …

  Dim variable  As Double
  Const multiplier As Double = 2.5
  variable = 2.0 ' VB changes this to 2# because It Knows Best 

  Multiply variable, multiplier

But, if you call it like this …

   Multiply (variable, multiplier)

The VB syntax checker doesn't like the parentheses and highlights the line in red to indicate its anger. The IDE may pop a message box with this enigmatic admonishment: Compile Error Expected:=

However, you may invoke the subroutine like this, enclosing the arguments in parentheses and using the Call keyword …

  Call Multiply (variable, multiplier)

With the Call keyword, the VB syntax checker accepts the parentheses. Here's a quotation from the MVBA online help that may, or may not, clarify this point for you …

You are not required to use the Call keyword when calling a procedure. However, if you use the Call keyword to call a procedure that requires arguments, argumentlist must be enclosed in parentheses. If you omit the Call keyword, you also must omit the parentheses around argumentlist.

Functions

Suppose we have a simple Function that modifies a variable. This example multiplies the passed variable by a passed value and returns the new value …

Function Multiply (ByVal original As Double, ByVal value As Double)
  Multiply = original * value
End Function

You might call the Function like this …

  Dim variable  As Double
  Const multiplier As Double = 2.5
  variable = 2.0 ' VB changes this to 2# because It Knows Best 

  Multiply variable, multiplier

The code is perfectly valid and perfectly useless. The syntax is fine, so the VB editor doesn't complain. However, you're not using the returned value, so why invoke the function?

Let's try again, this time assigning the value returned by the function to a variable …

  Dim variable  As Double
  Dim result  As Double
  Const multiplier As Double = 2.5
  variable = 2.0 ' VB changes this to 2# because It Knows Best 

  result = Multiply variable, multiplier	

VB doesn't like that syntax for a function: you must enclose the function arguments in parentheses when you assign the result. Here's the correct invocation …

  Dim variable  As Double
  Dim result  As Double
  Const multiplier As Double = 2.5
  variable = 2.0

  result = Multiply (variable, multiplier)

The VB syntax requires the parentheses around the arguments to a function call. Here's a quotation from the MVBA online help that may, or may not, clarify this point for you …

You are not required to use the Call keyword when calling a procedure. However, if you use the Call keyword to call a procedure that requires arguments, argumentlist must be enclosed in parentheses. If you omit the Call keyword, you also must omit the parentheses around argumentlist. If you use either Call syntax to call any intrinsic or user-defined function, the function's return value is discarded.

The last sentence means that, if you Call a function, there's no way to obtain its return value. So this invocation is pointless if you want to know the answer …

  Dim variable  As Double
  Dim result  As Double
  Const multiplier As Double = 2.5
  variable = 2.0

  Call Multiply (variable, multiplier) ' Where's my return value?

By the way, a Function defined in a class is known as a Method of that class. The world of object-oriented-design (OOD) has invented its own jargon: refuse to be intimidated!


Back to contents

Quoted Strings and File Names

Q  How do I tell MicroStation to use a file when the file name includes spaces?

A  We often need to tell MicroStation about a file when changing a setting. For example, when you want to assign a colour table you must pass the full path of the table file. When the file path contains spaces you must wrap the entire path in double-quote characters. Otherwise, the spaces confuse MicroStation.

Here's a simple function that wraps a string in double-quote characters …

'-------------------------------------------------------------
'   DoubleQuote
'   Wraps a string in double-quote marks
'-------------------------------------------------------------
Public Function DoubleQuote(ByVal s As String) As String
    Const sDOUBLE_QUOTE  As String = """"
    DoubleQuote = sDOUBLE_QUOTE & s & sDOUBLE_QUOTE
End Function

Here's how you might call that function …

Dim fileName As String
fileName = "N:\Projects\New Project\Color Tables\masterplan.tbl"
Debug.Print "Raw file name: " & fileName
Dim quotedName As String
quotedName = DoubleQuote (fileName)
Debug.Print "Quoted file name: " & quotedName

Dim keyin As String
keyin = "print pentable attach " + quotedName
Debug.Print "Keyin: " & keyin

'   Assign MicroStation color table
CadInputQueue.SendCommand keyin

Back to contents

Change a MicroStation Setting

Q  How do I change MicroStation setting XYZ using VBA?

A  Record a VBA macro as described while you manually change the setting. Usually, you will find some cryptic code in the recording that you can copy and paste into another macro.


Back to contents

Objects & User-Defined Types

Q I'm confused by objects and User-Defined Types. They both support dot notation, but how do I know when and where to put that dot?

A MVBA makes extensive use of objects and User-Defined Types (UDTs). MicroStation's geometric element types are represented by classes: when you use a variable that represents a class you have created an object. The Set keyword is exclusive to objects. If you omit the Set keyword, your code will compile but not execute: you will receive a run-time error …

  Dim oLine  As LineElement	' LineElement is a class provided by MVBA
  Dim vertices (0 To 1) As Point3d	' Point3d is a user-defined-type provided by MVBA
  ' Assign values to vertex 1 and vertex 2
  vertices(0).X = 10
  vertices(0).Y = 10
  vertices(0).Z = 0
  vertices(1).X = 20
  vertices(1).Y = 20
  vertices(1).Z = 0

  ' The next two lines are accepted by the VB editor	
  Set oElement = Application.CreateLineElement1 (Nothing, vertices)
  oElement = Application.CreateLineElement1 (Nothing, vertices)	' Run-time error!

As you probably noticed in the above example, we used an array of Point3d to supply a vertex list to the CreateLineElement1 method. A Point3d is a UDT pre-defined by MVBA. It's equivalent to this definition in your own code …

Type Point
  X As Double
  y As Double
  z As Double
End Type

A UDT is used like a primitive data type such as an Integer, Double, etc. It's not a class, and it's incorrect to confuse it with a class …

  '  Declare a variable of type Point	
  Dim point As Point

  '  Use a point variable	
  point.X = 1234
  point.Y = 2345
  point.Z = 0

Unfortunately, the VB editor doesn't complain when you confuse a UDT with a class. It accepts this code when you type it but rejects at compile-time …

  Dim point As Point

  '  Use a point variable incorrectly with Set keyword	
  Set point.X = 1234
  Set point.Y = 2345
  Set point.Z = 0

The worst thing about this error is the misleading message from VB: Compile error: Object required. The wording makes you think that you've used an object incorrectly, whereas the real mistake is attempting to use a UDT as if it were an object.

Because UDTs are similar to built-in data types, you can legally assign a variable to another variable provided each is declared as the same UDT. You can't do this with an object …

  '  Declare two variables of type Point	
  Dim point1 As Point
  Dim point2 As Point

  point1.X = 1234
  point1.Y = 2345
  point1.Z = 0

  '  Assign one point to another	
  point2 = point1	'  point2 now has the same X, Y, Z values as  point1

Note that I've defined the Point type purely for illustration. With MVBA, you should liberally sprinkle your code with variables declared as Point3d & Point2d data types. Those types are pre-defined for you by the kind folks at Bentley Systems.


Back to contents

Test for File Exists

Q How do I test whether a file really exists?

A Microsoft's Scripting Runtime library is invaluable. It provides …

  1. A useful set of folder & file functions using the File System Object
  2. A valuable Dictionary for storing a keyed collection of arbitrary objects

Here's a short example that illustrates how to tell if a file exists or not. You provide a full file path, and the function tells you (by its Boolean True or False result) if the file exists …

' ---------------------------------------------------------------------
'   FileExists
'   The code in this procedure uses the Microsoft Scripting Runtime library.
'   Use the Tools|References menu to add that library (scrrun.dll) to your VBA project
'   Returns: True if the file exists
' ---------------------------------------------------------------------
Public Function FileExists(ByVal fileName As String) As Boolean
    Dim oFileSystem                         As New Scripting.FileSystemObject
    FileExists = oFileSystem.FileExists(fileName)
    Set oFileSystem = Nothing
End Function

Debugging Assistance

Q How do I verify during development that some value is correct?

A VB and MVBA make it easy to be a sloppy programmer. However, help is at hand! The Debug object provides two useful methods: Debug.Print and Debug.Assert. Both methods only work while you, the programmer, are stepping through code in debug mode or running from the IDE. Your user will not see the results of the Debug object.

Debug.Print does what it says on the tin: it prints whatever text you tell it to print. The text is written to VBA's Immediate window.

Debug.Assert tests whatever you pass to it for Boolean True or False. If the argument fails the test (i.e. it evaluates to False) then your program halts. Here's an example that verifies that you have passed a valid file name …

' ---------------------------------------------------------------------
'   Start here: this procedure first tests a file that should exist,
'   regedit.exe in your Windows folder (typically C:\Windows).
'   Next it tests for a non-existent file:
'   VBA will halt executiong during debug at the Debug.Assert statement
' ---------------------------------------------------------------------
Sub TestAssert()
    AssertExample Environ("WINDIR") & "\regedit.exe"  ' File exists
    AssertExample "C:\nonexistent"                    ' File does not exist
End Sub
' ---------------------------------------------------------------------
Sub AssertExample(ByVal fileName As String)
    Debug.Print "Assert test on '" & fileName & "'"
    Debug.Assert FileExists(fileName)
End Sub

Back to contents

Insist on Variable Declaration

Q I used to be a sloppy programmer, but I want to reform my style. How do I go about achieving that goal?

A VB and MVBA make it easy to be a sloppy programmer. One way is to let you use a variable without declaring it first …

  skunk = 1234	  ' skunk is an Integer
  skunk = 1234.5  ' skunk is a Double
  skunk = "abc"   ' skunk is a String

Save your self trouble! Put the Option Explicit statement at the beginning of every module you write …

Option Explicit
  skunk = 1234	  ' Compile Error: Variable not defined

VB now wants you to declare each and every variable, so the compiler knows its data type and can detect an inappropriate assignment. Well, most of the time …

Option Explicit
  Dim skunk As Integer
  skunk = 1234	  ' OK
  skunk = 1234.5  ' Runs, but what is in variable skunk now?
  skunk = "abc"	  ' Run-time error

The second assignment was to test if you're still awake. The compiler accepts the assignment of a Double value (1234.5) to the Integer variable skunk. At run-time, VB truncates the floating-point number to fit into your Integer variable, so skunk contains the value 1234. In other words, your data have been silently mangled at run-time.

You can automate Option Explicit in the Options dialog of the VB IDE: select this from the Tools menu and toggle the check-box Require Variable Declaration. Once you've done this, VB writes Option Explicit at the start of each new module …


Back to contents

Initialise your Variables

Q I place a cell using VBA, but it appears as a dot. What happened?

Dim scale As Point3d
Dim origin As Point3d

origin.x = 100
origin.y = 200
origin.z = 0

Dim oCell As CellElement
Set oCell = Application.CreateCellElement2("example", origin, scale, False, Nothing)

A Visual Basic for Applications (VBA) tries to be your friend. It initialises variables for you. Unfortunately, VBA knows nothing about 3D geometry: it is ignorant about points, vectors, transforms, and matrices. If you initialise your scale variable to zero (or let VBA initialise it to zero for you), then any object scaled using that variable will have zero size. The proper initialisation for a scale is unity. MicroStation VBA offers the convenient method Point3dOne, which sets the X, Y and Z members of a Point3d data type to 1.0.

What happened in the above example is that the author declared a Point3d variable named scale to store the scale of the cell. Probably, she planned to set scale to a suitable value as she revised her code. Unfortunately, VBA initialised scale to zero. Placing a cell with scale zero results in a rather small object. MicroStation shows it as a dot, and you can use the Analyse Element tool to see that it really is a cell, but with negligible dimensions.

Probably the default value for scale should be unity, which you can achieve like this …

Dim scale As Point3d
scale = Point3dOne	' all components 1.0

Another trap for the uninitiated is a rotation matrix. A rotation in MicroStation VBA is expressed not in degrees but by a Transform. The Transform data type is provided with MicroStation VBA to store rotation matrices and other transform matrices. In 3D it's inconvenient to use simple rotations (and 2D is just a special case of 3D, but you knew that). Unfortunately, a Transform initialised to zero has no meaning: it's mathematically incompetent. If you let VBA initialise a Transform for you, then expect unexpected results. Instead, assign a meaningful value to your rotation variable like this …

Dim rotation As Transform
rotation = Transform3dIdentity	' zero rotation

Now, you can use the properly initialised scale and rotation variables …

Dim scale As Point3d
scale = Point3dOne	' all components 1.0

Dim rotation As Transform
rotation = Transform3dIdentity	' zero rotation

Dim origin As Point3d

origin.x = 100
origin.y = 200
origin.z = 0

Dim oCell As CellElement
Set oCell = Application.CreateCellElement2("example", origin, scale, False, rotation)

With meaningful initialisation of variables scale and rotation, the cell placement works as expected.


Back to contents

Windows Caption

Q How do I set the caption of MicroStation's main window?

A All VBA applications provide an Application object. The Application object is the root of the VBA hierarchy and is the interface with Windows. The MicroStation main window is a Windows window, if you'll pardon the excessive fenestration. Therefore, use the Application object to change the main window caption.

Customised caption for MicroStation's main window

Here's a code sample, probably the simplest you'll find on this page …

Sub TitleTest()
    SetWindowCaption "LA Solutions VBA Example"
End Sub
Sub SetWindowCaption(ByVal caption As String)
    Application.caption = caption
End Sub

Back to contents

Windows Prompts for Installation CD

Q Why does Windows prompt me for an installation CD when I run my VBA macro?

A The problem arises because various products that install VBA may have implemented slightly different versions. Microsoft's installer is attempting to repair what it thinks is a damaged Office installation. Because MicroStation delivers VBA, it's causing a version clash with Office.

Microsoft have provided an installer patch to fix this problem. There's more information in this Bentley Tech Note.

Maximum Number of Points in a Line

Q What is the maximum number of vertices in a line or shape?

A The answer is 5,000 although I don't see this mentioned in the VBA help. However, it is documented in MDL and there's even a function to return the value, so we can use it in VBA like this …

'  MDL function declaration 
Declare PtrSafe Function mdlElement_maxLineStringPoints Lib "stdmdlbltin.dll" ( ) As Long

'  Return the maximum vertex count in a line or shape 
Function MaxLineStringPoints () As Integer
  MaxLineStringPoints = mdlElement_maxLineStringPoints ()
End Function

'  Test vertex count function 
Sub TextMaxVertexCount ()
  Debug.Print "Max line vertex count=" & CStr(MaxLineStringPoints())
End Sub

If you want to create a shape or line having more than the maximum number of vertices, you should create a number of lines (ChainableElements) having 5,000 or fewer vertices, then combine them into a ComplexStringElement or ComplexShapeElement. The following VBA methods will help you …

Does a Named Cell Exist in a Library?

Q How can I tell if a named cell exists in a library?

A Before you create a CellElement, you want to be sure it exists in a currently-attached Cell Library.

This article about cells and VBA shows you how to check that a cell exists in a cell library.


Back to contents

Windows DLL Search Paths

Sometimes you need to use a function implemented in a Dynamically Linked Library (DLL). For example, you can use Win32 Windows functions by calling functions implemented in Microsoft's kernel32.dll, which you will find in the C:\Windows\System32 folder. Or you might want to use an MDL function, provided by one of the DLLs in the \MicroStation folder. If your VBA project is located in some other folder, how does VBA find the DLL that implements the function you want to call?

Q How Does Windows Find a DLL?

A Windows has a well-defined search order when it looks for a DLL …

  1. The directory where the executable module for the current process is located
  2. The current directory
  3. The Windows system directory. The GetSystemDirectory function retrieves the path of this directory
  4. The Windows directory. The GetWindowsDirectory function retrieves the path of this directory
  5. The directories listed in the PATH environment variable

Here's the original DLL search article on the Microsoft web site!

However, Microsoft has complicated things further by inventing a safe DLL search mode. Safe DLL search mode is intended to reduce security risks. Unfortunately, from the VBA developer's point of view, it adds complexity. The key difference is that the current folder is searched after the system folders.

  1. The directory from which the application loaded
  2. The system directory. Use the GetSystemDirectory function to get the path of this directory
  3. The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched
  4. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory
  5. The current directory.
  6. The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path

Here's the DLL safe search article on the Microsoft web site!

Calling MDL Functions from another Application's VBA

You can complicate things by calling an MDL function in VBA code written in another application. MicroStation VBA lets you call MDL functions. Sometimes you need to do this to obtain some obscure functionality that is not available in VBA.

For example, suppose you want to use MDL function mdlModelRef_isActiveModel. First, you must declare it, near the beginning of your VBA module, like this …

Declare PtrSafe Function mdlModelRef_isActiveModel Lib "stdmdlbltin.dll" ( ByVal modelRef As Long ) As Long

The above declaration informs VBA of the arguments the function requires, its return value, and the library (Lib) where it can be found. In this case the library is stdmdlbltin.dll, which you will find in the MicroStation folder.

VBA can also find stdmdlbltin.dll because that DLL is in the same folder as ustation.exe, the MicroStation executable that owns the memory context in which VBA executes. If the DLL were somewhere else, then VBA might not be able to find it.

Suppose you are writing a VBA macro in Excel. You have correctly referenced MicroStationDGN.Application, so Excel can find the MicroStation VBA objects. However, Excel doesn't know how to find MicroStation DLLs. If your VBA code contains a call to an MDL function, then Excel VBA probably won't find it.

You have to tell Excel where it can find the MicroStation DLLs. To accomplish that, add the location of ustation.exe to the Windows PATH variable. Then Excel VBA will be able to find the DLL, following the rules of the Windows Search Paths.


Back to contents

Divide a String into Words

Q How do I extract the words from a text string?

A You need VB's Split function. Split converts a text string into an array of strings. It uses a separator string that you specify to work out how the source string should be carved up.

A common requirement is to divide a string where the words are separated by space characters. You would write code something like this  …

Dim strSource       As String
Dim strWords()      As String
Const strSeparator  As String = " "

strSource = "Divide this string into words"
strWords  = Split (strSource, strSeparator)

Dim i               As Integer, _
    nWords          As Integer

nWords = UBound (strWords) - LBound (strWords) + 1

Debug.Print "strWords array contains " & CStr (nWords) & " words"

For i = LBound (strWords) To UBound (strWords)
  Debug.Print "Word [" & CStr (1 + i) & "]='" & words(i) & "'"
Next i

The opposite of Split, by the way, is Join.

Finding a File

Q How can I find a file using VB?

A The MDL mdlFile_find function searches for files in folders indicated by a configuration variable. There's an article about finding files using that MDL function.

Dates: Formatting and Calculating

Q VB's Format() and Date() functions provide answers to common questions along these lines …

A Suppose you want to obtain today's date: VB and VBA provide a Date data type and various functions that manipulate dates. The Now() function returns the current date and time from your computer's clock. Here's an example …

Sub TodaysDate()
    Dim strDate     As String
    Dim oDate       As Date

    oDate = Now()

    strDate = Format(oDate, "Medium Date")	' or "Short Date" or "Long Date" or your own custom date format
    Debug.Print "Today's date: " & strDate
End Sub

Here are some examples of date format strings for the date Friday, 20 January 2017 …

Format Comment Example Result
yyyy 4-digit year Format (Now(), "yyyy") 2017
yy 2-digit year Format (Now(), "yy") 17
mmmm full month name Format (Now(), "mmmm") January
mmm short month name Format (Now(), "mmm") Jan
mm numeric month Format (Now(), "mm") 01
dddd full day name Format (Now(), "dddd") Friday
ddd short day name Format (Now(), "ddd") Fri
dd numeric day Format (Now(), "dd") 20
dd-mmm-yyyy custom date Format (Now(), "dd-mmm-yyyy") 20-Jan-2017
mmm/dd/yy custom date Format (Now(), "mmm/dd/yy") Jan/20/17
yyyymmdd custom date Format (Now(), "yyyymmdd") 20170120

A VB and VBA provide a number of functions that operate on Date data. For example, if you want to calculate the date 90 days from now, you could use the DateAdd() function. The DateDiff() function calculates the difference between two dates. The following example shows the difference between today's date and the fourth of July 2007 …

Function ElapsedTimeSince4thJuly2007() As Long
    Dim strDate     As String
    Dim oDate1      As Date, _
        oDate2      As Date
    Dim elapsed     As Long

    oDate1 = #7/4/2007#
    oDate2 = Now()

    elapsed = DateDiff("d", oDate1, oDate2) ' Compute difference in days

    If (0 < elapsed) Then
    	Debug.Print "It is " & CStr(elapsed) & " days after " & Format(oDate1, "Medium Date")
    Else
    	Debug.Print "There are " & CStr(elapsed) & " days before " & Format(oDate1, "Medium Date")
    End If

    ElapsedTimeSince4thJuly2007 = elapsed
End Function

Back to contents

Element Range

Q How do I set an element's range in VBA?

A The range of an element is an imaginary box around the object. MicroStation uses each element's range to acquire summary information when it doesn't need to know the exact details of an element's geometry. For example, when updating a view, MicroStation needs to know which elements are contained by that view: it's far quicker to perform an approximate calculation using each element's range than to perform some complex geometrical analysis.

The MDL function mdlElmdscr_validate() recalculates an element's range. Here's how to call it from VBA …

' ---------------------------------------------------------------------
'   MDL function declarations (copy this section to the beginning of your VBA module)
' ---------------------------------------------------------------------
Declare Sub mdlElmdscr_validate Lib "stdmdlbltin.dll" (ByVal p As Long, ByVal modelRef As Long)

' ---------------------------------------------------------------------
'   SetElementRange
'   Invoke MDL function to validate an element, which has the effect of
'   recalculating the element's range
'   Returns: Nothing
' ---------------------------------------------------------------------
Sub SetElementRange(ByVal oElement As Element, ByVal oModel As ModelReference)
    mdlElmdscr_validate oElement.MdlElementDescrP, oModel.MdlModelRefP
End Sub

Back to contents

Formatting Numbers

The note about formatting numbers has been expanded and moved to its own numeric formatting with VBA page. It illustrates Microsoft's Format function.


Back to contents

Convert a Curve (or any other element) to a Line String

Q  How do I convert an Arc or Curve element to a Line String?

A  The Element.ConstructVertexList() method provides a way to simplify or stroke a curve to an array of vertices. Once you have an array of vertices, you can analyse it or convert it into another element, such as a Line String or B-Spline curve.

If you have an MDL background, then we believe that ConstructVertexList() is a wrapper around the MDL function mdlElmdscr_stroke().

Here's a suggestion from Gerald Hernandez, a frequent contributor to the Bentley Discussion Groups on how to use ConstructVertexList() to convert an Arc to a Line String …

Function StrokeArc(ByVal oArc As ArcElement, ByVal tolerance as Double) As LineElement
  Dim strokePoints() as Point3d

  strokePoints = oArc.ConstructVertexList (tolerance)
  '	Create a Line String using the original Arc as a template.
  '	The new Line String inherits the properties and symbology of the Arc
  Set StrokeArc= CreateLineElement1(oArc, strokePoints)

  Erase strokePoints
End Function

The tolerance argument tells the function how closely the array of points should match the original element. The finer the tolerance, the closer the result is to the original. When stroking an Arc, for example, you might set the tolerance to some fraction of the Arc's radius.

Recording a VBA Macro

Q  How do I record a VBA macro?

A  You can record a VBA macro from the VBA Projects dialog.

Open the VBA Projects dialog from the MicroStation menu Utilities|Macro|Project Manager. Select a project to make the Record button active, then perform a few operations using MicroStation's normal commands. Press the Stop button when you want to finish recording, and click the Edit button. You may have to hunt through the project file in the editor to find the macro you just recorded. VBA assigns an arbitrary name to your macro, and if you record several it's easy to become confused.

Hint: start the VBA editor, then rename your macro to something memorable as soon as you press the Stop button.

Using the Scroll Wheel in the VBA IDE

Q  How do I use the Scroll Wheel on my mouse in the VBA IDE?

A  VBA is showing its age: the Microsoft code base predates the arrival of mice having a scroll wheel.

The problem lies firmly in Microsoft's court. Fortunately, they have come up with a solution. Visit this Microsoft link to find how to enable the Scroll Wheel in the VBA IDE.

Pointers in Visual Basic

Q  How do I use pointers in VB?

A  Sometimes you just have to do those C things in VB. Here are some ideas from the Code Project.


Questions

Post questions about MicroStation programming to the MicroStation Programming Forum.