microstationv8iss1vbaforadvancedusers trn014750 1 0001

104
MicroStation VBA for Advanced Users MicroStation V8i (SELECTseries 1) Bentley Institute Course Guide TRN014750-1/0001

Upload: vthiyagain

Post on 28-Jan-2016

186 views

Category:

Documents


13 download

DESCRIPTION

microstation

TRANSCRIPT

TRN014750-1/0001

MicroStation VBA for Advanced UsersMicroStation V8i (SELECTseries 1)

Bentley Institute Course Guide

Trademarks

AccuDraw, Bentley, the “B” Bentley logo, MDL, MicroStation and SmartLine are registered trademarks; PopSet and Raster Manager are trademarks; Bentley SELECT is a service mark of Bentley Systems, Incorporated or Bentley Software, Inc. 

AutoCAD is a registered trademark of Autodesk, Inc. 

All ther brands and product names are the trademarks of their respective owners. 

Patents

United States Patent Nos. 5,8.15,415 and 5,784,068 and 6,199,125. 

Copyrights

©2000‐2009 Bentley Systems, Incorporated. MicroStation ©1998 Bentley Systems, Incorporated. All rights reserved. 

MicroStation VBA for Advanced Users ii Dec-09Copyright © 2009 Bentley Systems, Incorporated

Dec-09

Table of Contents

Course Overview ____________________________________ 1Course Description ____________________________________1Target Audience_______________________________________1Prerequisites _________________________________________1Course Objectives _____________________________________2Modules Included _____________________________________2

Building Commands  _________________________________ 3Module Overview _____________________________________3Module Prerequisites __________________________________3Module Objectives_____________________________________4Introductory Knowledge ________________________________4

Questions ________________________________________4Answers__________________________________________4

Adding Elements – PrimitiveCommands____________________4Modifying Elements – LocateCommands ___________________7Module Review _______________________________________9

Questions ________________________________________10

The User Interface ___________________________________ 11Module Overview _____________________________________11Module Prerequisites __________________________________11Module Objectives_____________________________________11Introductory Knowledge ________________________________11

Questions ________________________________________12Answers__________________________________________12

Building a User Interface ________________________________12Module Review _______________________________________15

Questions ________________________________________15

Working With Non‐Graphic Data  _______________________ 17Module Overview _____________________________________17Module Prerequisites __________________________________17Module Objectives_____________________________________17Introductory Knowledge ________________________________17

iii Table of ContentsCopyright © 2009 Bentley Systems, Incorporated

Dec-09

Table of Contents

Questions ________________________________________18Answers__________________________________________18

Storing Non‐Graphic Information With Elements_____________18Tags ________________________________________________19Databases ___________________________________________21

Connecting _______________________________________21Elements with database records ______________________24

XML ________________________________________________27UserData ____________________________________________27Xdata _______________________________________________28Module Review _______________________________________32

Questions ________________________________________32

Extending Functionality  ______________________________ 33Module Overview _____________________________________33Module Prerequisites __________________________________33Module Objectives_____________________________________33Introductory Knowledge ________________________________33

Questions ________________________________________33Answers__________________________________________34

CExpression Processor__________________________________34Calling C and MDL Functions _____________________________36

Using MDL functions in VBA classes ____________________38Helpful notes______________________________________52

Module Review _______________________________________55Questions ________________________________________55

Events  ____________________________________________ 57Module Overview _____________________________________57Module Prerequisites __________________________________57Module Objectives_____________________________________57Introductory Knowledge ________________________________58

Questions ________________________________________58Answers__________________________________________58

Types of Events _______________________________________58File events ________________________________________59Interfaces ________________________________________59

Module Review _______________________________________62Questions ________________________________________62

Standards Checker Extensions  _________________________ 63Module Overview _____________________________________63Module Prerequisites __________________________________63Module Objectives_____________________________________63Introductory Knowledge ________________________________63

iv Table of ContentsCopyright © 2009 Bentley Systems, Incorporated

Dec-09

Table of Contents

Questions ________________________________________64Answers__________________________________________64

Implementation_______________________________________64Simple – use the basic framework _____________________65Advanced – use the Fixes optional dialog________________66

Building Custom Standards Checker Applications ____________66Questions ________________________________________76

Module Review Answers  _____________________________ 77Building Commands____________________________________77

Questions ________________________________________77Answers__________________________________________77

User Interface ________________________________________78Questions ________________________________________78Answers__________________________________________78

Working With Non‐Graphic Data _________________________79Questions ________________________________________79Answers__________________________________________79

Extending Functionality_________________________________79Questions ________________________________________79Answers__________________________________________80

Events ______________________________________________80Questions ________________________________________80Answers__________________________________________80

Standards Checker Extensions____________________________81Questions ________________________________________81Answers__________________________________________81

Appendix  __________________________________________ 83New to MicroStation VBA? ______________________________83

The MicroStationDGN Object Model ___________________83System Overview______________________________________84

Configuration Variables for VBA_______________________84Keyins for VBA_____________________________________85

COM Client Applications ________________________________86ActiveX Controls and DLLs _______________________________87Interface Programming _________________________________88Sample Macros _______________________________________88

Scanning _________________________________________88Geometry ________________________________________89Working with other files _____________________________96Dynamic user interface______________________________97

v Table of ContentsCopyright © 2009 Bentley Systems, Incorporated

Table of Contents

Table of Contents vi Dec-09Copyright © 2009 Bentley Systems, Incorporated

Course Overview

Course Description

This course serves as an introduction to MicroStation VBA for application developers with working knowledge of Visual Basic/VBA and as a follow‐on to the MicroStation VBA Essentials course for those with prior experience with MicroStation VBA.

Regardless of a student’s background, this course provides an essential foundation for the design, development, and deployment of MicroStation VBA macros automating fundamental operations in MicroStation.

Target Audience

The primary audience for this course is Application Developers — analysts and programmers. This course may also be valuable for the following audience(s):

• Administrators

• Consultants

• Managers

• Planners

Prerequisites

• Have either a working knowledge of Visual Basic/VBA or successfully completed the MicroStation VBA Essentials course. If the former, you should review the section “New to MicroStation VBA?” in the Appendix to this course guide before proceeding with this course.

• Familiarity with basic MicroStation concepts such as design files, models, references, and levels.

Dec-09 1 Course Overview

Copyright © 2009 Bentley Systems, Incorporated

Course Objectives

• Familiarity with popular MicroStation tools and view controls.

• Basic understanding of the MicroStation object model —in particular, the Application, Attachment, DesignFile, Element, and ModelReference objects. 

Course Objectives

After completing this course, you will be able to:

• Work with MicroStation's VBA objects to automate fundamental operations in the product.

• Deploy VBA macros in MicroStation.

Modules Included

The following modules are included in this course:

• Building Commands

• User Interface

• Working With Non‐Graphic Data

• Extending Functionality

• Events

• Standards Checker Extensions

Course Overview 2 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Building Commands

Module Overview

One of the basic building blocks of applications in MicroStation is the command structure. VBA opens this up to application developers through interfaces. The two interfaces used for commands are IPrimitiveCommandEvents and ILocateCommandEvents.

The command interfaces allow macros to follow a consistent structure. The structure of the command is designed to work with the state machine format that is built into MicroStation. By developing a collection of command classes, a set of tools can be integrated in the workspace.

The basic structure for the macro is to define a subroutine in a module that will instantiate a class that implements one of the interfaces. The interface will provide the entry points for MicroStation to call, that the class will use to react to user actions. Interface programming is covered in depth in the module “Standards Checker Extensions”.

Module Prerequisites

• Familiarity with the concepts of placing, locating, manipulating and modifying elements in a model and the applicable tools.

Refer to the Course Overview for additional pre‐requisites.

Dec-09 3 Building Commands

Copyright © 2009 Bentley Systems, Incorporated

Module Objectives

Module Objectives

After completing this module, you will be able to:

• Implement commands that add elements to a model.

• Implement commands that interactively find and query or modify elements in a model.

Introductory Knowledge

Before you begin this module, let's define what you already know.

Questions

1 What does a VBA project file contain?

2 In a model scan what kind of object will return only elements that match search criteria?

3 True or False: Hardly any MicroStation menu selections have a corresponding key‐in you can use to directly invoke it.

Answers

1 Macros, including modules, classes, and forms which, in turn, contain subroutines and functions.

2 ElementScanCriteria

3 False. Most MicroStation menu selections and tools have a corresponding key‐in you can use to directly invoke it.

Adding Elements – PrimitiveCommands

The IPrimitiveCommandEvents are typically used in commands that will be adding elements to a model.

Primitive commands will use the data point and reset actions to interact with the user. The interface also defines entry points for the start of the command if key‐

Building Commands 4 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Adding Elements – PrimitiveCommands

ins are to be picked up and used, along with command termination through the cleanup event. The following is the structure of a class that implements the IPrimitiveCommandEvents interface.

Option Explicit

Implements IPrimitiveCommandEvents

Private Sub IPrimitiveCommandEvents_Cleanup()End Sub

Private Sub IPrimitiveCommandEvents_DataPoint(Point As Point3d, _ByVal View As View)

End Sub

Private Sub IPrimitiveCommandEvents_Dynamics(Point As Point3d, _ByVal View As View, ByVal DrawMode As MsdDrawingMode)

End Sub

Private Sub IPrimitiveCommandEvents_Keyin(ByVal Keyin As String)End Sub

Private Sub IPrimitiveCommandEvents_Reset()End Sub

Private Sub IPrimitiveCommandEvents_Start()

End Sub

The IPrimitiveCommandEvents_Start method is called when this command class is invoked. Typically this method will initialize any settings that the command may need to set the initial prompts for the user. 

The IPrimitiveCommandEvents_Keyin method is active if the usesKeyins option is True when the command class is instantiated. This will allow the user to type into MicroStation’s key‐in line while this command is active, for the class to use. 

The IPrimitiveCommandEvents_Reset method is called when the user presses the Reset button. This is useful when a command wants to step backward. 

The IPrimitiveCommandEvents_Cleanup method is called when the command is terminated by another command being started. 

The IPrimitiveCommandEvents_Dynamics method is active when the CommandState.startDynamics is called. This method is used to let the command show reaction to mouse movements. 

The IPrimitiveCommandEvents_DataPoint method is called when the user enters a data point. 

Dec-09 5 Building CommandsCopyright © 2009 Bentley Systems, Incorporated

Adding Elements – PrimitiveCommands

These methods combine to produce the standard workflow used by a command that is adding an element to a model.

In the next exercise a cell is to be built on the fly in the code.

Exercise: Implement a command that will add a cell to a model

1 Launch MicroStation and open any design file.

2 Select Utilities > Macros > Project Manager.

3 Create and load the project Exercise_1.

4 Open the Visual Basic Editor.

5 Insert a class module and rename it Exercise1.

6 Type Implements IPrimitiveCommandEvents.

7 Type the function CreateSimpleGraphic that will be used to create a simple graphic cell.

Function CreateSimpleGraphic(oPoint As Point3d) As CellElement

Dim oShape As ShapeElement

Dim oElements(0) As Element

Dim oCell As CellElement

Dim ptsCorners(3) As Point3d

ptsCorners(1).X = 10

ptsCorners(2).X = 10

ptsCorners(2).Y = 10

Building Commands 6 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Modifying Elements – LocateCommands

ptsCorners(3).Y = 10

Set oShape = CreateShapeElement1(Nothing, ptsCorners, msdFillModeFilled)

Set oElements(0) = oShape

Set oCell = CreateCellElement1("Simple", oElements, Point3dZero, False)

Set CreateSimpleGraphic = oCell

End Function

8 Add the methods, including the processing logic, for the IPrimitiveCommandEvents interface.

9 Add logic to the DataPoint method that calls CreateSimpleGraphic and then displays and adds the cell to the model.

10 Switch to Module1, which is a normal module rather than a class module, and add a subroutine named PlaceCell that will serve as an entry point for the macro.

11 In PlaceCell, create a new instance of the class Exercise1, using the line:

CommandState.startPrimitive new Exercise1

12 Run the macro.

Press <F5> in the VBA Editor and return to MicroStation.

Note: Often there is more than one way to create an element. Consider what information you will be gathering from users to create the element, then decide on the method.

Modifying Elements – LocateCommands

The ILocateCommandEvents are typically used to interactively find and query or modify elements.

Locate commands will use the “locate accept and process” algorithm for finding elements. The interface defines entry points for validating the located element and cleaning up after the command. Following is the structure of a class that implements the ILocateCommandEvents interface.

Dec-09 7 Building CommandsCopyright © 2009 Bentley Systems, Incorporated

Modifying Elements – LocateCommands

Option ExplicitImplements ILocateCommandEvents

Private Sub ILocateCommandEvents_Accept(ByVal Element As Element, _Point As Point3d, _ByVal View As View)

End Sub

Private Sub ILocateCommandEvents_Cleanup()End Sub

Private Sub ILocateCommandEvents_Dynamics(Point As Point3d, _ByVal View As View, _ByVal DrawMode As MsdDrawingMode)

End Sub

Private Sub ILocateCommandEvents_LocateFailed()End Sub

Private Sub ILocateCommandEvents_LocateFilter(ByVal Element _As Element, _Point As Point3d, _Accepted As Boolean)

End Sub

Private Sub ILocateCommandEvents_LocateReset()End Sub

Private Sub ILocateCommandEvents_Start()End Sub

The CommandState.createLocateCriteria method can be used to create the locate criteria object that will be used to filter, at a high level, the selectable elements.  Once the LocateCriteria is created, use the include/exclude methods to set the features of the object. Then the CommandState.setLocateCriteria is called to put the filter in place. 

In the ILocateCommandEvents_LocateFilter method, the macro has the ability to preview the element. If it fails some test, then the element can be rejected. 

If the user identifies an element that does not meet the requirements of the locate criteria, the ILocateCommandEvents_LocateFailed method is called. In this method, the macro can notify the user of the error condition. 

ILocateCommandEvents_LocateReset is used when an element has been selected, but the user has pressed the Reset button.  In this case, many macros can release some of the resources that have accumulated during the locate process. 

Building Commands 8 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Module Review

The Cleanup method is called when the command is terminated by starting another command.  This can be used to release resources that have been consumed by this command. 

The ILocateCommandEvents_Dynamics method can be activated by calling the CommandState.startDynamics method. Dynamics can be used to show actions like moving an element or other incremental changes that are a result of moving the mouse or pointer.  

The ILocateCommandEvents_Accept method is called when the user enters a data point confirming the acceptance of the element(s) that have been selected.

In the next exercise you will implement a command that lets the user identify an element and then opens a dialog showing the type of element selected.

Exercise: Implement a locate command

1 Continuing in the project Exercise_1, insert a new class module and rename it Exercise2.

2 Type Implements ILocateCommandEvents.

3 Add the methods, including the processing logic, for the ILocateCommandEvents.

4 Add logic to the Accept method that will get the type and set a MsgBox to display this information.

5 Switch to Module1, and add a new subroutine named GetElementType that will serve as the entry point for the module.

6 In GetElementType, create a new instance of the class Exercise2, using the line:

commandState.startLocate new Exercise2

7 Run the macro.

Exercise: Implement a locate command with specified scan criteria

1 Add a LocateCriteria to the start method in Exercise2.

Module Review

Now that you have completed this module, let’s measure what you have learned.

Dec-09 9 Building CommandsCopyright © 2009 Bentley Systems, Incorporated

Module Review

Questions

1 What are events?

2 What are the events that are available to the IPrimitiveCommandEvents interface?

3 What are the events that are available to the ILocateCommandEvents interface?

4 How does one make use of an interface?

Building Commands 10 Dec-09Copyright © 2009 Bentley Systems, Incorporated

The User Interface

Module Overview

Building dialogs can begin as a simple task, but adding advanced features to enhance the user experience can take quite a bit of work.

The UserForm set that is used in VBA is limited by design. To add features such as resizing and menus, macros have to make use of the Win32 API.

Module Prerequisites

• Familiarity with the concept of locating elements in a model and the applicable tools.

• Familiarity with the native VBA tools available for building a UserForms.

Refer to the Course Overview for additional pre‐requisites.

Module Objectives

After completing this module, you will be able to:

• Build dialog boxes with functionality more advanced than that available through the native VBA UserForm controls.

Introductory Knowledge

Before you begin this module, let's define what you already know.

Dec-09 11 The User Interface

Copyright © 2009 Bentley Systems, Incorporated

Building a User Interface

Questions

1 When creating a Message Box, how do specify its buttons be labeled “Yes” and “No”?

2 Which does the ShowModal property of a UserForm determine?

3 How do you initiate the creation of a UserForm in a VBA project.

{First multiple choice/fill in the blank answer.}

{Second multiple choice/fill in the blank answer.}

{Third multiple choice/fill in the blank answer.}

Answers

1 Specify vbYesNo as the second parameter of the call to the MsgBox function.

2 The ShowModal property determines whether the form is modal.

3 In the Visual Basic Editor, select Insert > UserForm.

Building a User Interface

When building a user interface, a developer must be mindful of how the user thinks. If the user is a visual person, the user interface needs to use graphics to convey a message. Some controls that may be useful are the SpinButton, ScrollBar, and Image Control. The SpinButton and ScrollBar can be used for manipulating the image that is displayed in the Image control. The Image Control can be used to preview design graphics. To do this when working with MicroStation elements, the dialog needs to use the GetPicture method from the element class. 

Dynamic contents allow the user interface to reflect the current set of information. Building a macro that has a picture of each cell in an attached cell library is an example of a macro that must create components on the fly. To do this, the macro creates a collection of buttons to add to the dialog, and the dialog must resize so they fit.

TabPages are a method to make sense of large sets of related information. To start with, TabPages are defined in a set of either TabStrip or MultiPage items. The TabStrip displays the same set of controls on multiple pages, or tabs. The 

The User Interface 12 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Building a User Interface

MultiPage item displays a different set of controls on multiple pages. Tab pages can be added to the UserForm at runtime. 

The information may also be generated while the macro is running. To make pages of the tab set visible, macros need to work with the tabpage.visible property. To make a form resizable the macro needs to add the following declarations.

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _(ByVal lpClassName As String, _ByVal lpWindowName As Any) As Long

Private Declare Function GetWindowLong Lib "user32" Alias _"GetWindowLongA" _(ByVal HWND As Long, _ByVal nIndex As Long) As Long

Private Declare Function SetWindowLong Lib "user32" Alias _"SetWindowLongA" _(ByVal HWND As Long, _ByVal nIndex As Long, _ByVal dwNewLong As Long) As Long

Private Const WS_THICKFRAME = &H40000

Private Const GWL_STYLE = (-16)

Then when the form is activated the active method will need to call these functions to change the style of the dialog that is used:

Dim lngFrmHWND As Long

Dim lngStyle As Long

Dim lngRetVal As Long

lngFrmHWND = FindWindow("ThunderDFrame", Me.Caption)

lngStyle = GetWindowLong(lngFrmHWND, GWL_STYLE)

lngRetVal = SetWindowLong(lngFrmHWND, GWL_STYLE, _ lngStyle Or WS_THICKFRAME)

Finally, when the resize event occurs, the macro will need to take care of adjusting the contents of the dialog to the new area.  An example follows.

Private Sub UserForm_Resize()

ListBox2.Left = 1

ListBox2.Width = Me.Width - 9

ListBox2.Height = Me.Height / 2 - 5

Dec-09 13 The User InterfaceCopyright © 2009 Bentley Systems, Incorporated

Building a User Interface

Label1.Top = ListBox2.Height + 2

Label1.Height = 5

Label1.Width = ListBox2.Width

ListBox1.Top = ListBox2.Height + Label1.Height + 5

ListBox1.Height = Me.Height / 2 - 10

ListBox1.Left = ListBox2.Left

ListBox1.Width = ListBox2.Width

Debug.Print "Height: " & Me.Height & " Width:" & Me.Width

Debug.Print "the upper list box is "; ListBox2.Height; " tall starts _at "; ListBox2.Top

Debug.Print "the lower list box is "; ListBox1.Height; " tall starts _at "; ListBox1.Top

End Sub

In the following exercise you will build a dialog which can be used by a locate macro to display the identified element.

Exercise: Build the dialog

1 Continuing with the project Exercise_1 from “Building Commands”, insert a userform and make it non‐modal.

2 Add an Image to the form.

3 Add a this method to the UserForm:

Sub DisplayElement(oElement As Element)

Dim stdPic As StdPicture

With PreviewImageArea

Set stdPic = oElement.GetPicture(PointsToPixelsX(.Width), _PointsToPixelsY(.Height), True)

.Picture = stdPic

End With

End Sub

The User Interface 14 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Module Review

4 In the ILocateCommandEvents_Accept method, call the UserForm1.DisplayElement method.

5 Integrate the Image Area into the locate macro by calling the Userform1.show method.

6 In the ILocateCommandEvents_Reset method, hide the form.

7 In the ILocateCommandEvents_Cleanup method, unload the form.

Module Review

Now that you have completed this module, let’s measure what you have learned.

Questions

1 Name at least five standard user interface controls available to VBA developers?

2 How can an Image Control be used in a dialog?

3 What methods must be included to make a form resizable?

Dec-09 15 The User InterfaceCopyright © 2009 Bentley Systems, Incorporated

Module Review

The User Interface 16 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Working With Non‐Graphic Data

Module Overview

Many applications require that a graphic element be supported by non‐graphic information. MicroStation supports many options to accomplish this and there are pros and cons for each solution. A developer must take these into account when deciding how to store information.

Module Prerequisites

• Understanding of the concept of associating non‐graphic information with graphic elements and the applicable tools.

Refer to the Course Overview for additional pre‐requisites.

Module Objectives

After completing this module, you will be able to:

• Create VBA code to attach and extract tag data.

• Create VBA code to create and maintain database linkages.

• Create VBA code to set and get Xdata.

Introductory Knowledge

Before you begin this module, let's define what you already know.

Dec-09 17 Working With Non-Graphic Data

Copyright © 2009 Bentley Systems, Incorporated

Storing Non-Graphic Information With Elements

Questions

1 What types of non‐graphic information can be associated with a graphic element?

2 What is ODBC?

3 True or False: MicroStation provides a utility to convert data records from an external database, which is linked to the active design file, into tag data.

Answers

1 Tags

Database linkages

UserData

Xdata

XML

2 ODBC is a standard generic interface that allows applications to access SQL compliant databases. MicroStation supports ODBC database linkages among others.

3 True. The utility is the Database to Tag Convertor (DBTOTAG).

Storing Non‐Graphic Information With Elements

Adding non‐graphic information allows for “smart” elements. And since graphics do not always tell the whole story, you can store extra, non‐graphic, information with elements. There are a number of methods for doing this, so the application developer will have to choose the method that works best for their needs.

• If pre‐defined tools will be used or the information is relatively simple, tags are a likely choice.

• If the information needs to be shared outside of MicroStation, then a database is the obvious choice.

• If the information needs to be protected from editing, user data should be used.

• If the information needs to be shared with an AutoCAD application, Xdata is the best choice.

Working With Non-Graphic Data 18 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Tags

Another option is XML, an emerging standard for information interchange that will see more tools and development in MicroStation.

Tags

Tags provide a method to link some fixed structure, non‐graphic, data to an element. The tag process adds an element, which can be optionally displayed, to a model. The information contained in a tag element can be edited with standard MicroStation tools. 

Elements must be added to a file before a tag can be added to an element. Tags do not directly modify the element in the model. They keep a reference to the base element so it can be found. The tag itself is defined by a TagSet. 

Exercise: Scanning a model for elements with tags.

1 In MicroStation, open the file Database.dgn in the folder ...\WorkSpace\Projects\Examples\General\dgn.

The model Tags contains tagged elements.

2 In the VBA Project Manager dialog, create and load the project Tags.

3 Switch to the Visual Basic Editor, insert a module, and create the following macro:

Sub TagReport()

Dim oScP As ElementScanCriteria

Dim oEnum As ElementEnumerator

Dim oTagDefs As TagDefinitions

Dim oTagSet As TagDefinition

Dim oTag As TagElement

Dim strFileName As String

Dim tmpString As String

Dim outString As String

Dim oModel As ModelReference

strFileName = ActiveDesignFile.FullName

tmpString = Replace(strFileName, ".dgn", ".txt")

Open tmpString For Output As 1

Set oScP = New ElementScanCriteria

oScP.ExcludeNonGraphical

Dec-09 19 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated

Tags

For Each oModel In ActiveDesignFile.Models

outString = "*** Scanning model " & oModel.Name & " ***"

Print #1, outString

Set oEnum = oModel.Scan(oScP)

Do While oEnum.MoveNext

Dim oEl As Element

If oEnum.Current.HasAnyTags Then

Dim oTagsArray() As TagElement

Dim i As Long

Set oEl = oEnum.Current

oTagsArray = oEl.GetTags

For i = LBound(oTagsArray) To UBound(oTagsArray)

If (i >= 1) Then

If (oTagsArray(i - 1).TagSetName <> _oTagsArray(i).TagSetName) Then

outString = _"Found Tag Set = " & _oTagsArray(i).TagSetName

Debug.Print outString

Print #1, outString

End If

Else

outString = "Found Tag Set = " & _oTagsArray(i).TagSetName

Print #1, outString

End If

outString = "--- " & _oTagsArray(i).TagDefinitionName & _" = " & oTagsArray(i).Value

Print #1, outString

Next

Print #1,

End If

Loop

Print #1, "--------------------------"

Next

Close 1

Debug.Print “TagReport processing is complete”

Working With Non-Graphic Data 20 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Databases

End Sub

4 Run the macro TagReport.

The macro creates the text file Database.txt in the same folder as the design file and writes to the text file the TagSetName and the tag data for each element.

5 Open Database.txt in a text editor such as Notepad to review the output.

Exercise: Tag an element

1 Add a tag to a selected element.

There are good examples in the MicroStation VBA help document, Objects, Tag Objects.

Databases

Databases offer a flexible storage system for non‐graphic information. The structure of the information must be determined by the database or application designer. Applications need to maintain the validity of information, since the database may not always be connected. 

To work with database information, MicroStation has to connect to the data source first, then work with the information that is stored on an element. The information is kept external to MicroStation so it is available to other non‐MicroStation users through other applications.

Connecting

One of the connection types available to VBA developers is the ADO data connection. The macro needs to create an ADOConnection, then set the connection string. The connection string is the service provider type, the DSN name, and, for some types of service providers — for example, Oracle — the user name or id and the password. 

Note:  The provider type is the software provider for the database driver, such as Oracle, OLEDB, or MSDASQL.

To complete the next exercise, first make the GIS ODBC data source provided with MicroStation available on the system you are using, as follows:

Dec-09 21 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated

Databases

1 In the Windows Control Panel, select Administrative Tools > Data Sources (ODBC).

2 In the ODBC Data Source Administrator dialog, select the User DSN tab (or, if you have administrative rights you can use the System DSN tab) and click Add.

3 In the Create New Data Source dialog, select the Microsoft Access Driver (*.mdb) and click Finish.

4 In the ODBC Microsoft Access Setup dialog, type GIS as the data source Name and, optionally, add a description of your choice.

5 Click Select.

6 Navigate to the ...\WorkSpace\System\macros directory and select gis.mdb. You may need to copy and paste the full path to gis.mdb in the Database File field.

7 Click OK.

8 In the ODBC Microsoft Access Setup dialog, click OK.

9 In the ODBC Data Source Administrator dialog, click OK.

Exercise: Connect to the GIS ODBC data source

1 In the VBA Project Manager dialog, create and load the project ODBC_Datasource.

2 Switch to the Visual Basic Editor and insert a module that contains declarations of variables needed to connect to the data source:

'Global variables for connecting to the database

Public DNSname As String

Public UIDPWD As String

Public ADOconn As ADODB.Connection

3 Insert a UserForm, frmDBConnect, and add to it the following text field and command button objects:

txtDNSName — DSN Name

cmdDBConnect — Connect

cmdCancel — Cancel

4 Double‐click the Connect button object, and type the following code to handle a click of the button:

Private Sub cmdDBConnect_Click()

On Error GoTo RDOCC_EH

Working With Non-Graphic Data 22 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Databases

'accept values from the user

DNSname = txtDNSName.Value

If DNSname <> "" Then

UIDPWD = "Provider=MSDASQL;"

UIDPWD = UIDPWD & "DSN="

UIDPWD = UIDPWD & DNSname

Set ADOconn = New Connection

ADOconn.ConnectionString = UIDPWD

ADOconn.Open

MsgBox ("Opened connection.")

End If

Unload Me

Exit Sub

RDOCC_EH:

MsgBox ("ERROR " & Err.Number & " " & Err.Description)

End Sub

5 Type the following code to handle a click of the Cancel button:

Private Sub cmdCancel_Click()

End

End Sub

6 Select Tools > References.

7 In the References dialog, select Microsoft ActiveX Data Objects 2.5 Library and Microsoft ADO Ext. 2.8 for DDL and Security in addition to the libraries already selected, then click OK.

8 Run the form.

9 In the DSN Name field, type GIS.

10 Click Connect.

Note:  In the ...\WorkSpace\System\vba\examples folder, the VBA project files DataBaseExamples.mvba and dbcheck.mvba are for use with an Oracle database. The userform for connecting to the data source is therefore augmented to require a username and password.

Dec-09 23 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated

Databases

Elements with database records

When working with graphic elements that are connected to database records, the process to extract the non‐graphic information is more complex. The element only stores a pair of numeric ids.

• The Entity Number, which is related to a database table by looking in the MSCATALOG table.

• The MSLink which is the unique value stored in a column of the table.

The macro must first query the MSCATALOG table to get the tablename from the Entity Number. Once the table name is known, the macro can then query that table based on the MSLink number to get the information from the table. To work with the data, the macro will build a RecordSet that will be used to hold the results of the queries.  Some of the queries that you need will appear as follows.

'Get the Table name for a given entity.

Public Function GetTableName(MSCATstr As String, EntNum As Integer) _As String

On Error Resume Next

Dim RecordCount As Integer

Dim RecSet1 As adodb.Recordset

RecordCount = 1

'take this out for access since the USER_TABLES table does not exist!

If dbtype = "Oracle" Then

' Set RecSet1 = GetRecordSet(ADOconn, "SELECT TABLE_NAME FROM

‘ USER_TABLES WHERE TABLE_NAME = '" + MSCATstr + "'")

' RecordCount = RecSet1.RecordCount

End If

If RecordCount < 1 Then

'no entry found for given tablename

MsgBox "MSCATALOG Not Found!"

RecSet1.Close

Set RecSet1 = Nothing

GetTableName = ""

ElseIf RecordCount = 1 Then

RecordCount = 0

Dim RecSet2 As adodb.Recordset

Working With Non-Graphic Data 24 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Databases

Set RecSet2 = GetRecordSet(ADOconn, "SELECT TABLENAME FROM " + _MSCATstr + " WHERE ENTITYNUM = " & CStr(EntNum))

RecordCount = RecSet2.RecordCount

If RecordCount = 0 Then

MsgBox "No Table for Entity: " & CStr(EntNum), vbOKOnly

RecSet2.Close

Set RecSet2 = Nothing

GetTableName = ""

ElseIf RecordCount = 1 Then

GetTableName = RecSet2!TableName

RecSet2.Close

Set RecSet2 = Nothing

Else

MsgBox "Multiple Tables for Entity: " _& CStr(EntNum), vbOKOnly

RecSet2.Close

Set RecSet2 = Nothing

GetTableName = ""

End If

Else

MsgBox "MSCATALOG is not Unique!"

RecSet1.Close

Set RecSet1 = Nothing

GetTableName = ""

End If

End Function

Public Sub PrintLink(LinkIndex As Integer)

'prints database info for a given dblink

Dim mslink As Integer

Dim MSCATstr As String

Dim EntNum As Integer

Dim TableName As String

Dim RS1 As adodb.Recordset

Dim i As Integer

If oElement.HasAnyDatabaseLinks Then

Dec-09 25 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated

Databases

'check to see if element has database link

mslink = dbLinks(LinkIndex).mslink

EntNum = dbLinks(LinkIndex).EntityNumber

MSCATstr = GetMSCATALOG

TableName = GetTableName(MSCATstr, EntNum)

If TableName <> "" Then

Set RS1 = GetRecordSet(ADOconn, _"SELECT * FROM " + TableName + " _WHERE MSLINK = " & CStr(mslink))

Dim RecordCount As Integer

RecordCount = RS1.RecordCount

If RecordCount = 0 Then

' no records found with matching mslink number

Debug.Print "No Records Found!!"

ElseIf RecordCount = 1 Then

' one entry found for given mslink number

For i = 0 To RS1.Fields.count - 1

Debug.Print RS1.Fields.Item(i).Name

frmDBLinkInfo.ListBox1.List(i, 1) = "NULL"

Next

Else

' multiple entries found for mslink number

Debug.Print "MSLINK is not Unique!!"

End If

RS1.Close

Set RS1 = Nothing

End If

Else

' element does not have any database links

Debug.Print "Element has no DB Links"

End If

End Sub

Exercise: Obtain database information

1 Create a macro to query an element and get the database information that is related to the element.

Working With Non-Graphic Data 26 Dec-09Copyright © 2009 Bentley Systems, Incorporated

XML

XML

XML is a technology for creating self‐documented structured storage of information. XML can be used to create text files that can be interpreted by any macro that is capable of parsing XML data.

Note:  As of this writing, the XML API in VBA is limited to using the Microsoft MSXML6 libraries. In the future, methods will be provided to add XML as additional element information.

UserData

UserData is extra information that is added to an element by a macro. The structure of the information is determined by the macro. 

The macro is assigned an ID number by Bentley. The ID number is used to prevent macro conflict. To work with user data, the macro will need to first get the user data as a block of information. Then it will need to process the block of data using the CopyXXX method. The Copy methods use the copyToDataBlock parameter to determine if the data is being copied to or from the datablock. 

' Do not use 22352 as your attribute ID. You must obtain a

' unique attribute ID from Bentley Systems.

Private Const attrId As Long = 22352

' AddLinkage and GetLinkage both transfer the data using TransferBlock.

' That way, it is easy to be certain that the transfer always occur in the

' same order.

Private Sub TransferBlock(dblk As DataBlock, _name As String, _value As Long, _copyToDataBlock As Boolean)

dblk.CopyString name, copyToDataBlock

dblk.CopyLong value, copyToDataBlock

End Sub

Sub AddLinkage()

Dim ele As Element

Dec-09 27 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated

Xdata

Dim id As DLong

Dim dblk As New DataBlock

id = DLongFromLong(50296)

Set ele = ActiveModelReference.GetElementByID(id)

TransferBlock dblk, "Added by User Attributes Example", 50296, True

ele.AddUserAttributeData attrId, dblk

ele.Rewrite

End Sub

Sub GetLinkage()

Dim ele As Element

Dim id As DLong

Dim dblk() As DataBlock

Dim value As Long, name As String

id = DLongFromLong(50296)

Set ele = ActiveModelReference.GetElementByID(id)

dblk = ele.GetUserAttributeData(attrId)

TransferBlock dblk(0), name, value, False

MsgBox "NAME: " & name & ", VALUE: " & value

End Sub

Note: When macros use the CopyString method, VBA will copy a Unicode String to the element.  If the macro needs to be compatible with MDL the macro needs to convert the String to an Array of bytes, then use the CopyByteArray method.

Xdata

Another method that applications can use to store information is Xdata. Xdata is a format that AutoCAD uses to store extra information on elements. This has the advantage of making the information compatible with the DWG format.

Sub InterpretXData(Xdata() As XDatum)

Dim I As Long

Dim J As Long

Dim D As DLong

Dim Lev As Level

Working With Non-Graphic Data 28 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Xdata

Dim vt As Variant

' Using Debug.Print, display all of the XData to the Immediate Window of

' the Visual Basic Editor.

For I = LBound(Xdata) To UBound(Xdata)

With Xdata(I)

Debug.Print GetXDatumName(.Type) & " --- ";

If VarType(.Value) = vbEmpty Then

Debug.Print "the value is empty"

Else

Select Case .Type

Case msdXDatumTypePoint, _msdXDatumTypeWorldDirection, _msdXDatumTypeWorldSpaceDisplacement, _msdXDatumTypeWorldSpacePosition

' Value is of the type Point3d.

Debug.Print .Value.X; .Value.Y; .Value.Z

Case msdXDatumTypeDatabaseHandle

' Value is a hex string. Get the element

' ID by calling DLongFromHexString

Debug.Print "&H" & .Value

Dim eleID As DLong

eleID = DLongFromHexString(.Value)

Case msdXDatumTypeBinaryData, _msdXDatumTypeUnsupported

' Value is of the type Byte().

For J = LBound(.Value) To UBound(.Value)

Debug.Print Hex(.Value(J)); " ";

Next J

Case Else

' Value is of a type that can be printed directly.

Debug.Print .Value

End Select

End If

End With ' Xdata(I)

Next I

End Sub

Dec-09 29 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated

Xdata

Sub ShowEleXData()

' Find all of the XData in any element

Dim ee As ElementEnumerator

Set ee = ActiveModelReference.GraphicalElementCache.Scan

Do While ee.MoveNext

If ee.Current.HasAnyXData Then

Dim appNames() As String

Dim index As Long

appNames = ee.Current.GetXDataApplicationNames

For index = LBound(appNames) To UBound(appNames)

Dim aXdata() As XDatum

aXdata = ee.Current.GetXData(appNames(index))

InterpretXData aXdata

Next

End If

Loop

End Sub

Sub ShowAllModelXData()

' Find all XData contained on any model in the active design file

Dim theModel As ModelReference

Dim appNames() As String

Dim index As Long

For Each theModel In ActiveDesignFile.Models

If theModel.HasAnyXData Then

Debug.Print "-----Reporting XData for model " & _theModel.Name & " -----"

appNames = theModel.GetXDataApplicationNames

For index = LBound(appNames) To UBound(appNames)

Dim aXdata() As XDatum

Debug.Print "----- Application " & _appNames(index) & "-----"

aXdata = theModel.GetXData(appNames(index))

Working With Non-Graphic Data 30 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Xdata

InterpretXData aXdata

Next

End If

Next

End Sub

Function GetXDatumName(xdType As MsdXDatumType) As String

' Translates an XData type into a String

Select Case xdType

Case msdXDatumTypeBinaryData

GetXDatumName = "Binary Data"

Case msdXDatumTypeControlString

GetXDatumName = "Control String"

Case msdXDatumTypeDatabaseHandle

GetXDatumName = "Database Handle"

Case msdXDatumTypeDistance

GetXDatumName = "Distance"

Case msdXDatumTypeInt16

GetXDatumName = "Int16"

Case msdXDatumTypeInt32

GetXDatumName = "Int32"

Case msdXDatumTypeLevel

GetXDatumName = "Level"

Case msdXDatumTypePoint

GetXDatumName = "Point"

Case msdXDatumTypeReal

GetXDatumName = "Real"

Dec-09 31 Working With Non-Graphic DataCopyright © 2009 Bentley Systems, Incorporated

Module Review

Case msdXDatumTypeScaleFactor

GetXDatumName = "Scale Factor"

Case msdXDatumTypeString

GetXDatumName = "String"

Case msdXDatumTypeUnsupported

GetXDatumName = "Unsupported"

Case msdXDatumTypeWorldDirection

GetXDatumName = "World Direction"

Case msdXDatumTypeWorldSpaceDisplacement

GetXDatumName = "World Space Displacement"

Case msdXDatumTypeWorldSpacePosition

GetXDatumName = "World Space Position"

End Select

GetXDatumName = GetXDatumName & "(" & xdType & ")"

End Function

Module Review

Now that you have completed this module, let’s measure what you have learned.

Questions

1 What is the preferred method to work with an external database?

2 What is required to work with User Data?

3 What tools are used to work with XML data?

Working With Non-Graphic Data 32 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Extending Functionality

Module Overview

The “C” programming environment is available to applications in order to work with the data structures used in MicroStation. Applications work through the Get/SetCExpressionValue method of the Application Object to access the “C” expression evaluator that is in MicroStation’s runtime environment.  

Module Prerequisites

• Familiarity with MDL as a platform for developing programmed customizations for MicroStation.

Refer to the Course Overview for additional pre‐requisites.

Module Objectives

After completing this module, you will be able to:

• Create VBA code utilizing MDL functions.

Introductory Knowledge

Before you begin this module, let's define what you already know.

Questions

1 What are the components of an ADOConnection string?

Dec-09 33 Extending Functionality

Copyright © 2009 Bentley Systems, Incorporated

CExpression Processor

2 True or False: There are no practical limitations placed on the number of MDL applications that can be simultaneously loaded.

3 What is the MDL dialog in MicroStation used for and how do you open it?

Answers

1 Service provider type, DSN name, user name or id, password

2 True

3 The MDL dialog is used to load and unload MDL applications and to view technical details and key‐ins for MDL applications. To open it, select Utilities > MDL.

CExpression Processor

An expression is a valid programming statement as it would be put into a “C” program. The GetCExpressionValue method takes in the “C” expression and, optionally, the MDL application that is required.  The SetCExpressionValue method takes in the expression used to set some value in an MDL application. When an MDL function is not exposed to VBA, the Cexpression evaluator can often be used to call the function.  The Cexpression processor can be used to call MDL functions from external Visual Basic programs.

retValue GetCexpressionValue(“mdlPolygon_pointInsideXY (“ & _

VarPtr(pCentroid) & “,” & _

VarPtr(boundryPoints(0)) & “,” & _

numboundryPoints & “,” _

tol & ")")

Option Explicit

Implements IPrimitiveCommandEvents

Private Sub IPrimitiveCommandEvents_Cleanup()

End Sub

Private Sub IPrimitiveCommandEvents_DataPoint(Point As Point3d, _ByVal View As View)

Dim volumeP As Double

Extending Functionality 34 Dec-09Copyright © 2009 Bentley Systems, Incorporated

CExpression Processor

Dim areaP As Double

Dim closureP As Double

Dim iXYP As Double

Dim iXZP As Double

Dim iYZP As Double

Dim tol As Double

Dim pCentroid As Point3d

Dim pMoment As Point3d

Dim pPrincipalMoments As Point3d

Dim pPrincipalDirections As Point3d

Dim retValue As Long

Dim oElement As Element

tol = 1#

Set oElement = CommandState.LocateElement(Point, View, False)

retValue = 0

If Not oElement Is Nothing Then

If oElement.IsPlanarElement Or oElement.Type = _msdElementTypeBsplineSurface Then

retValue = _ GetCExpressionValue("mdlMeasure_surfaceProperties (" & _ VarPtr(areaP) & "," & _ VarPtr(pCentroid) & "," & _ VarPtr(pMoment) & "," & _ VarPtr(iXYP) & "," & _ VarPtr(iXZP) & "," & _ VarPtr(iYZP) & "," & _ VarPtr(pPrincipalMoments) & "," & _ VarPtr(pPrincipalDirections) & "," & _ oElement.MdlElementDescrP & "," & _ tol & "," & " 0)")

Else

retValue = GetCExpressionValue("mdlMeasure_volumeProperties (" & _ VarPtr(volumeP) & "," & _

VarPtr(areaP) & "," & _ VarPtr(closureP) & "," & _ VarPtr(pCentroid) & "," & _ VarPtr(pMoment) & "," & _ VarPtr(iXYP) & "," & _ VarPtr(iXZP) & "," & _

Dec-09 35 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

VarPtr(iYZP) & "," & _ VarPtr(pPrincipalMoments) & "," & _ VarPtr(pPrincipalDirections) & "," & _ oElement.MdlElementDescrP & "," & _ tol & "," & " 0)")

End If

If retValue = 0 Then

Debug.Print "the centroid is _"; pCentroid.X; pCentroid.Y; pCentroid.Z

Else

Debug.Print "returned a "; retValue

End If

End If

End Sub

Private Sub IPrimitiveCommandEvents_Dynamics(Point As Point3d, _ByVal View As View, ByVal DrawMode As MsdDrawingMode)

End Sub

Private Sub IPrimitiveCommandEvents_Keyin(ByVal Keyin As String)

End Sub

Private Sub Class_Initialize()

End Sub

Private Sub IPrimitiveCommandEvents_Reset()

End Sub

Private Sub IPrimitiveCommandEvents_Start()

CommandState.SetLocateCursor

End Sub

Calling C and MDL Functions

One of the many features available to programmers in Visual Basic is the ability to import functions from other libraries. This can be done using the Declare 

Extending Functionality 36 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

statement and providing the prototype for the function call in a Visual Basic module. 

The syntax is as follows:

[Public | Private] Declare Sub subName Lib "libName.dll" [Alias "AliasName"] [ ( [ argumentList] ) ]

[Public | Private] Declare Function FuncName Lib "libName.dll" [Alias "AliasName"] [ ( [ argumentList] ) ] As Type

Public|Private defines the scope of the procedure. All declarations in a class module must be private. If neither Public or Private keyword is used, the declaration is implicitly Public.

The subName or FuncName must start with a letter, be unique within the scope, have less than 255 characters, and cannot be a VBA keyword,

The library must be the complete name including the .dll extension unless it is one of the standard Windows libraries. If the path is not specified then the search is executed using the following logic:

• The directory that the application was loaded

• The current working directory

• The Windows\System32 directory

• The Windows\System directory

• The PATH environment variable

Optionally, the “Alias” can be used to define the name as written in the original source code. That is the “subName” local to the VBA module, only the Alias is the name in the DLL.

The Argument list is the variable name and the type of information that the library needs. Some common conversions are as follows.

C DataType VBA DataType

BOOL ByVal fValue as Integer

BYTE ByVal bytValue as Byte

DWORD ByVal ingValue as Long

int ByVal intValue as Long

LPSTR ByVal strValue as String

Dec-09 37 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

Example declarations

Public Declare Function WinHelp Lib "user32" Alias "WinHelpA" _(ByVal hwnd As Long, _ByVal lpHelpFile As String, _ByVal lngCommand As Long, _dwData As Any) As Long

Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex _As Long) As Long

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _(ByVal lpClassName As String, _ByVal lpWindowName As Any) As Long

Private Declare Function GetWindowLong Lib "user32" Alias _"GetWindowLongA" (ByVal HWND As Long, _ByVal nIndex As Long) As Long

Private Declare Function SetWindowLong Lib "user32" Alias _"SetWindowLongA" (ByVal HWND As Long, _ByVal nIndex As Long, _ByVal dwNewLong As Long) As Long

Private Const WS_THICKFRAME = &H40000

Private Const GWL_STYLE = (–16)

Private Sub UserForm_Activate()

Dim lngFrmHWND As Long

Dim lngStyle As Long

Dim lngRetVal As Long

lngFrmHWND = FindWindow("ThunderDFrame", Me.Caption)

lngStyle = GetWindowLong(lngFrmHWND, GWL_STYLE)

lngRetVal = SetWindowLong(lngFrmHWND, GWL_STYLE, _lngStyle Or WS_THICKFRAME)

End Sub

Using MDL functions in VBA classes

You can use MDL functions in VBA classes by importing libraries delivered with the MicroStation SDK. 

Extending Functionality 38 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

• The stdmdlbltin.dll library provides access to the core mdl functions in MicroStation.

• The stdmdlaccessor.dll library provides some useful Element/Element descriptor functions.

The MicroStation SDK is available for download athttp://www.bentley.com/en‐US/Corporate/Bentley+Partner+Program/Technology+Partners/MicroStation+SDK.htm.

Some common MDL data types are:

In Visual Basic, an Integer is 16 bits and a Long is 32 bits. Passing an argument by reference generates a pointer to the variable.

Some examples of using these are as follows.

Levels

Option Explicit

Private Declare Function mdlLevel_setActiveByName Lib "stdmdlbltin.dll" _(ByVal parentLevelIdIn As Long, _ByVal pLevelNameIn As Long) As Long

Sub SetActiveLevelFromLibraryTest()

Dim ParentID As Long

Dim oLevelName As String

Dim status As Long

Dim oPtr As Integer

' this needs to end up as 0xffffffff

ParentID = &HFFFFFFFF

oLevelName = "G-CODE"

status = mdlLevel_setActiveByName(ParentID, StrPtr(oLevelName))

Debug.Print status

End Sub

C DataType VBA DataType

Dpoint3d ByRef pt As Point3d

Transform ByRef transP As Transform3d

DgnModelRefP ByVal modelRef As Long

MSElementDescr ByRef edPP As Long

Dec-09 39 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

Multi‐line placement

Const NULLPtr As Long = 0

Declare Function mdlMline_create Lib "stdmdlbltin.dll" _(ByVal mline As Long, _ByVal seed As Long, _ByRef normal As Point3d, _ByRef points As Point3d, _ByVal nPoints As Long) As Long

Declare Function mdlElmdscr_new Lib "stdmdlbltin.dll" _(ByRef elDescrPP As Long, _ByVal elemHeader As Long, _ByVal element As Long) As Long

Function CreateMultiLine() As element

Dim bytes(0 To 2000) As Byte

Dim vertices(0 To 4) As Point3d

Dim elmDescrP As Long

vertices(0) = Point3dFromXY(0,0)

vertices(1) = Point3dFromXY(1,1)

vertices(2) = Point3dFromXY(2,0)

vertices(3) = Point3dFromXY(3,1)

vertices(4) = Point3dFromXY(4,0)

mdlMline_create VarPtr(bytes(0)), NULLPtr, Point3dFromXYZ(0,0,1), _vertices(0), 5

mdlElmdscr_new elmDescrP, NULLPtr, bytes(0)

Set CreateMultiLine = MdlCreateElementFromElementDescrP(elmDescrP)

End Function

Multi‐line information

Declare Sub mdlMline_getInfo Lib "stdmdlbltin.dll" _(ByRef nPoints As Long, _ByRef nLines As Long, _ByRef dist1 As Double, _ByRef dist2 As Double, _

Extending Functionality 40 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

ByRef zVector As Point3d, _ByVal mline As Long)

Declare Function ElmdscrAccessor_getMSElement Lib "stdmdlaccessor.dll" _(ByVal ElementDescr As Long) As Long

Sub DumpMLineInfo()

Dim ele As element

Dim pElement As Long

Dim elmDescr As Long

Dim nPoints As Long

Dim nLines As Long

Dim dDist1 As Double

Dim dDist2 As Double

Dim zVector As Point3d

Dim oScanCriteria As New ElementScanCriteria

Dim ElementEnum As ElementEnumerator

oScanCriteria.ExcludeAllTypes

oScanCriteria.IncludeType msdElementTypeMultiLine

Set ElementEnum = ActiveModelReference.Scan(oScanCriteria)

Do While ElementEnum.MoveNext

With ElementEnum.Current

elmDescr = .MdlElementDescrP

pElement = ElmdscrAccessor_getMSElement(elmDescr)

mdlMline_getInfo nPoints, nLines, dDist1, dDist2, _zVector, pElement

Debug.Print "nPoints = " & nPoints;

Debug.Print ", nLines = " & nLines;

Debug.Print ", dDist1 = " & dDist1;

Debug.Print ", dDist2 = " & dDist2

End With

Loop

End Sub

Note:  In MicroStation V8 XM Edition and later, there exists a PropertyHandler object in the VBA object model that provides read‐write access to the property system used by the Element Information tool. Using PropertyHandler is 

Dec-09 41 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

therefore a viable alternative not only for getting and setting element properties but also design file, model, and attachment properties. Because PropertyHandler works on a file's copy of an object, if a macro uses the object to change element properties, those changes are not automatically reflected in the Element object. The macro has to load the object again — perhaps by using the GetElementByID method — to get the changes. There are numerous examples of using PropertyHandler in the MicroStation VBA help document, Objects, PropertyHandler Object.

References

' Reference File Parameters

Const REFERENCE_DISPLAY = 1

Const REFERENCE_SNAP = 2

Const REFERENCE_LOCATE = 3

Const REFERENCE_SLOTACTIVE = 4

Const REFERENCE_SCALELINESTYLES = 5

Const REFERENCE_FILENOTFOUND = 6

Const REFERENCE_FILENAME = 7

Const REFERENCE_DESCRIPTION = 8

Const REFERENCE_LOGICAL = 9

Const REFERENCE_SCALE = 10

Const REFERENCE_ROTATION = 11

Const REFERENCE_ATTACHNAME = 13

Const REFERENCE_SCALE_MASTERUNITS = 15

Const REFERENCE_RESERVEDA = 16

Const REFERENCE_HIDDEN_LINE = 18

Const REFERENCE_DISPLAY_HIDDEN = 19

Const REFERENCE_CLIP_ROTATE = 20

Const REFERENCE_WCHAR_DESCRIPTION = 21

Const REFERENCE_WCHAR_LOGICAL = 22

Const REFERENCE_SCALE_STORED = 23

Const REFERENCE_SCALE_BY_UNITS = 24

Const REFERENCE_ANONYMOUS = 25

Const REFERENCE_OWNINGMODELREF = 26

Const REFERENCE_SOURCEMODELID = 27

Const REFERENCE_REFNUM = 28

Const REFERENCE_ELEMENTID = 29

Extending Functionality 42 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

Const REFERENCE_DISPLAYRASTERREFS = 30

Const REFERENCE_WCHAR_MODELNAME = 31

Const REFERENCE_USE_LIGHTS = 32

Const REFERENCE_DONOTNEST = 33

Const REFERENCE_CLIPBACK = 34

Const REFERENCE_CLIPFRONT = 35

Const REFERENCE_NESTDEPTH = 36

Const REFERENCE_RENDERMODE = 37

Const REFERENCE_REDUNDANT = 38

Const REFERENCE_LEVEL_OVERRIDES = 39

Const REFERENCE_DISPLAYFILENAME = 40

Const REFERENCE_DISPLAYATTACHNAME = 41

Const REFERENCE_DISPLAYMODELNAME = 42

Const REFERENCE_DWGBLOCKNAME = 43

Const REFERENCE_DONTDETACHONALL = 44

Const REFERENCE_DISPLAYFLAG = 45

Const REFERENCE_MODELNOTFOUND = 46

Const REFERENCE_CLIP_ROTMATRIX = 47

Const REFERENCE_LEVEL = 48

Const REFERENCE_DWGUNITMODE = 49

Const REFERENCE_HSVVALUEADJUST = 50

Const REFERENCE_HSVSATURATIONADJUST = 51

Const REFERENCE_BASENESTDEPTH = 52

Const REFERENCE_RIGHTNOTGRANTED = 53

Const REFERENCE_PRINTCOLORADJUST = 54

Const REFERENCE_METADATAONLY = 55

Const REFERENCE_EXTENDED = 56

Const REFERENCE_HSVHUESETTING = 57

Const REFERENCE_HSVADJUSTMENTFLAGS = 58

Const REFERENCE_DISPLAYPRIORITY = 59

Const REFERENCE_NAMEDGROUP = 60

Const REFERENCE_REVISION = 61

Const REFERENCE_TRANSPARENCY = 62

Const REFERENCE_PLOT_3D = 63

Const REFERENCE_NESTOVERRIDES = 64

Const REFERENCE_NEWLEVELDISPLAY = 65

Dec-09 43 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

Const REFERENCE_GLOBALLINESTYLESCALES = 66

Const REFERENCE_TREAT_AS_ELEMENT = 67

Const REFERENCE_PROVIDERID = 68

Const REFERENCE_RAWREVISION = 69

Const REFERENCE_REVISIONNOTFOUND = 70

Const REFERENCE_USEANNOTATIONSCALE = 71

Const REFERENCE_ATTACHMETHOD = 73

Const REFERENCE_ACTIVATESTATUS = 74

Const REFERENCE_SYNCHWITHSAVEDVIEW = 75

Const REFERENCE_USEVIEWFLAGS = 76

Const REFERENCE_SAVEDVIEWNAME = 77

Const REFERENCE_SAVEDVIEWELEMENTID = 78

Const REFERENCE_LEVELCONTROLSDISPLAY = 79

Const REFATTACH_NEST_NONE = 0

Const REFATTACH_NEST_COPY = 1

Const REFATTACH_NEST_DISPLAY = 2

Const REFCOLORTABLE_USEPREF = 0

Const REFCOLORTABLE_ALWAYS = 1

Const REFCOLORTABLE_NEVER = 2

Const REFERENCE_LOCATEON = 0

Const REFERENCE_LOCATEOFF = 1

Const REFERENCE_PARENTLOCATEOFF = 2

Const REFERENCE_NOLOCATERIGHTS = 3

Const REFERENCE_PARENTNOLOCATERIGHTS = 4

' Reference File mdl Functions

Declare Function mdlRefFile_attach Lib "stdmdlbltin.dll" _(ByRef outModelRefP As Long, _ByVal fileName As String, _ByVal logical As Long, _ByVal description As Long, _ByRef masterOrigin As Point3d, _ByRef referenceOrigin As Point3d, _ByVal scale As Double, _ByRef rotMatrix As Matrix3d, _ByVal nClipPoints As Long, _ByRef clipPoints As Point2d, _ByVal levelDisplayFlag As Long, _ByVal snapLock As Long, _ByVal locateLock As Long) As Long

Extending Functionality 44 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

Declare Function mdlRefFile_attachByView Lib "stdmdlbltin.dll" _(ByRef outModelRefP As Long, _ByVal fileName As String, _ByVal logical As Long, _ByVal description As Long, _ByRef viewName As Long, _ByVal scale As Double, _ByRef centerPoint As Point3d, _ByVal levelDisplayFlag As Long, _ByVal snapLock As Long, _ByVal locateLock As Long) As Long

Declare Function mdlRefFile_attachCoincident Lib "stdmdlbltin.dll" _(ByRef outModelRefP As Long, _ByVal fileName As String, _ByVal logical As Long, _ByVal description As Long, _ByVal levelDisplayFlag As Long, _ByVal snapLock As Long, _ByVal locateLock As Long) As Long

Declare Function mdlRefFile_attachCoincidentExtended Lib _"stdmdlbltin.dll" _(ByRef outModelRefP As Long, _ByVal fileName As String, _ByVal logical As Long, _ByVal description As Long, _ByVal levelDisplayFlag As Long, _ByVal snapLock As Long, _ByVal locateLock As Long, _ByVal callAsynchs As Long) As Long

Declare Function mdlRefFile_attachmentIdFromModelRef Lib _"stdmdlbltin.dll" _(ByVal modelRef As Long) As DLong

Declare Function mdlRefFile_beginAttachment Lib "stdmdlbltin.dll" _(ByRef outModelRefP As Long, _ByVal fileName As Long, _ByVal modelName As Long, _ByVal logical As Long, _ByVal description As Long) As Long

Declare Function mdlRefFile_completeAttachment Lib "stdmdlbltin.dll" _(ByVal modelRef As Long, _ByVal nestFlag As Long, _

Dec-09 45 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

ByVal nestDepth As Long, _ByVal initialDisplay As Long) As Long

Declare Function mdlRefFile_detach Lib "stdmdlbltin.dll" _(ByVal modelRef As Long) As Long

Declare Function mdlRefFile_getFromAttachmentID Lib "stdmdlbltin.dll" _(ByRef refFilePP As Long, _ByRef modelRefP As Long, _ByVal srcModelRef As Long, _ByVal attachmentID As DLong) As Long

Declare Function mdlRefFile_getInfo Lib "stdmdlbltin.dll" _(ByVal modelRef As Long) As Long ' Returns a pointer to a structure

Declare Function mdlRefFile_getLocateLock Lib "stdmdlbltin.dll" _(ByVal refP As Long) As Long

Declare Function mdlRefFile_getParameters Lib "stdmdlbltin.dll" _(ByVal param As Long, _ByVal paramName As Long, _ByVal modelRef As Long) As Long

Declare Function mdlRefFile_getParent Lib "stdmdlbltin.dll" _(ByVal refP As Long) As Long ' Returns a pointer to a structure

Declare Function mdlRefFile_getRefCount Lib "stdmdlbltin.dll" () As Long

Declare Function mdlRefFile_getSnapLock Lib "stdmdlbltin.dll"(ByVal refP As Long) As Long

Declare Function mdlRefFile_is3d Lib "stdmdlbltin.dll"(ByVal refP As Long) As Long

Declare Function mdlRefFile_isPrimary Lib "stdmdlbltin.dll"(ByVal refP As Long) As Long

Declare Function mdlRefFile_reattach Lib "stdmdlbltin.dll" _(ByVal modelRef As Long, _ByVal outName As String, _ByVal fileName As String, _ByVal ModelName As Integer) As Long

Extending Functionality 46 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

Declare Function mdlRefFile_reload Lib "stdmdlbltin.dll" _(ByVal modelRef As Long, _ByVal updateDisplay As Long, _ByVal forceReload As Long) As Long

Declare Function mdlRefFile_rotate Lib "stdmdlbltin.dll" _(ByVal modelRef As Long, _ByRef pivotP As Point3d, _ByVal xrotation As Double, _ByVal yrotation As Double, _ByVal zrotation As Double, _ByVal view As Long) As Long

Declare Function mdlRefFile_setClip Lib "stdmdlbltin.dll" _(ByVal modelRef As Long, _ByRef pts As Point2d, _ByVal nverts As Long) As Long

Declare Function mdlRefFile_setParameters Lib "stdmdlbltin.dll" _(ByVal param As Long, _ByVal paramName As Long, _ByVal modelRef As Long) As Long

Declare Function mdlRefFile_updateReference Lib "stdmdlbltin.dll" _(ByVal modelRef As Long, _ByVal displayMode As Long) As Long

Declare Function mdlRefFile_writeAttachment Lib "stdmdlbltin.dll" _(ByVal modelRef As Long) As Long

Declare Function mdlRefFile_writeAttachmentConditionally Lib _"stdmdlbltin.dll" _(ByVal modelRef As Long) As Long

Declare Function mdlMline_create Lib "stdmdlbltin.dll" _(ByVal mline As Long, _ByVal seed As Long, _ByRef normal As Point3d, _ByRef points As Point3d, _ByVal nPoints As Long) As Long

Dec-09 47 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

Declare Function mdlElmdscr_new Lib "stdmdlbltin.dll" _(ByRef elDescrPP As Long, ByVal elemHeader As Long, _ByVal element As Long) As Long

Const NULLPtr As Long = 0

Sub DumpRefData()

Dim activeModel As ModelReference

Dim refModel As ModelReference

Dim refModel2 As ModelReference

Dim refModels As ModelReferences

Dim attach As Attachment

Dim attchmnts As Attachments

Dim status As Long

Dim refP As Long

Dim modrefP As Long

Dim pts(0 To 4) As Point2d

Dim nverts As Long

Dim refDesc As String

Dim buffer(255) As Byte

Dim dgnfile As DesignFile

Dim param As Variant

Dim scalefactor As Double

scalefactor = ActiveModelReference.UORsPerMasterUnit

nverts = 5

pts(0).X = 0

pts(0).Y = 0

pts(1).X = 5 * scalefactor

pts(1).Y = 0

pts(2).X = 5 * scalefactor

pts(2).Y = 2 * scalefactor

pts(3).X = 0

pts(3).Y = 2 * scalefactor

pts(4).X = 0

pts(4).Y = 0

Extending Functionality 48 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

Dim ModelName As String

Dim LogicalName As String

Dim Description As String

ModelName = "Default"

LogicalName = "testing"

Description = "test file"

status = mdlRefFile_beginAttachment(modrefP, ActiveDesignFile.Name, _0, StrPtr(LogicalName), StrPtr(Description))

If status = 0 Then

status = mdlRefFile_setClip(modrefP, pts(0), nverts)

Debug.Print "Reference Clip Status = " & status

status = mdlRefFile_completeAttachment(modrefP, 2, -1, 0)

End If

End Sub

Declare Sub mdlElmdscr_setVisible Lib "stdmdlbltin.dll" _(ByVal edP As Long, _ByVal visible As Long)

Sub SetVisible()

Dim ele As element

Dim eleDescr As Long

Dim oScanCriteria As New ElementScanCriteria

Dim ElementEnum As ElementEnumerator

Set ElementEnum = ActiveModelReference.Scan(oScanCriteria)

Do While ElementEnum.MoveNext

Set ele = ElementEnum.Current

eleDescr = ele.MdlElementDescrP

mdlElmdscr_setVisible eleDescr, 0

If ele.FilePosition <> 0 Then

ele.Rewrite

End If

Dec-09 49 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

'now make it visible again

ActiveDesignFile.Views(1).Redraw

mdlElmdscr_setVisible eleDescr, 1

If ele.FilePosition <> 0 Then

ele.Rewrite

End If

ActiveDesignFile.Views(1).Redraw

Loop

End Sub

Element descriptors

Declare Function mdlACS_getCurrent Lib "stdmdlbltin.dll" _(ByRef originP As Point3d, _ByRef rotMatrixP As Matrix3d, _ByVal typeP As Long, _ByVal nameP As Long, _ByVal descriptionP As Long) As Long

Sub DoACS()

Dim ele As LineElement

Dim pntOrigin As Point3d

Dim pntStart As Point3d, pntEnd As Point3d

Dim rot As Matrix3d

Dim iType As Integer

Dim result As Long

Dim tms As Transform3d

result = mdlACS_getCurrent(pntOrigin, rot, iType, 0, 0)

rot = Matrix3dFromMatrix3dTimesMatrix3d(rot, _Matrix3dFromScale(ActiveModelReference.UORsPerMasterUnit))

rot = Matrix3dInverse(rot)

tms = Transform3dFromMatrix3dPoint3d(rot, pntOrigin)

' Create an element using this transform

pntStart = Point3dFromTransform3dTimesPoint3d(tms, Point3dFromXY(0,0))

pntEnd = Point3dFromTransform3dTimesPoint3d(tms, Point3dFromXY(3,3))

Extending Functionality 50 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

Set ele = CreateLineElement2(Nothing, pntStart, pntEnd)

ele.Redraw

ActiveModelReference.AddElement ele

End Sub

The method MdlCreateElementFromElementDescr will appear when you enable the Show hidden members option in the Object Browser.

Other methods available are:

• MdlGetModelReferenceFromModelRefP

• MdlGetDesignFileFromModelRefP

These will help bridge the gap between the two languages. In addition to stdmdlbltin.dll and stdmdlaccessor.dll, there are other libraries available that will expose most functions available to MDL in VBA. These are:

• stdbspline.dll for the Bspline curve and surface functions

• stdcons.dll for the constraint manager functions

• stdimage.dll for the image functions

• stdkisolid.dll for the kernel independent solid functions

• stdmtg.dll for the manifold topology functions

• stdraster.dll for the raster reference functions

• stdrdbms.dll for the database functions

• stdrender.dll for the rendering functions

One note about the functions that are in these libraries; since they are Pascal calling sequence functions, they do not handle variable argument lists. So, any function that uses a variable argument will not be available. The function templates are available from the MDL Function Reference help document (MDLAPIFunctionReference.chm) in the MicroStation SDK.

Excerpt from msdn.microsoft.com:

Traditionally, APIs are written for C and C++ programmers who are building Windows applications, but the functions in a DLL can be called by other programming languages, including VBA. Because most DLLs are written and documented primarily for C and C++ programmers, calling a DLL function 

Dec-09 51 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

might differ somewhat from calling a VBA function. To work with an API, you must understand how to pass arguments to a DLL function.

Caution: Calling the Windows API and other DLL functions can be hazardous to the health of your application. When you call a DLL function directly from your code, you are bypassing some of the safety mechanisms that VBA usually provides for you. If you make a mistake in defining or calling a DLL function (as all programmers eventually do), you might cause an application error (also referred to as a general protection fault, or GPF) in your application. The best strategy is to save your project before you run your code, and to make sure you understand the principles behind calls to DLL functions.

Helpful notes

This process should by no means be a substitute for using the Object Model. It should be contained in modules and tested thoroughly. There are no safety checks to help you with memory allocation issues, and you have no access to the fields of a data structure. 

You can use the VarPtr function to obtain a memory reference to a block of information. The drawback is that the type of data is unknown, and so, can be easily corrupted. An advantage can exist if a function can accept NULL as an input value.

To pass strings from VBA to mdl functions that require MSWChar*, you need to use the StrPtr method. This is because VBA tries to automatically convert String variables to char *.

The scope of these functions should be kept Private to the module so that the parameters can be adjusted for the specific case.

Exercise: Displaying a file selection dialog box using the function mdlDialog_fileOpen

1 If you have not already done so, download and install the MicroStation SDK fromhttp://www.bentley.com/en‐US/Corporate/Bentley+Partner+Program/Technology+Partners/MicroStation+SDK.htm

2 In the VBA Project Manager dialog, create and load the project Open_File.

3 Switch to the Visual Basic Editor, insert a module, and create the following macro, ShowOpen:

Extending Functionality 52 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

Private Declare Function mdlDialog_fileOpen Lib _"stdmdlbltin.dll" _(ByVal fileName As String, _ByVal rFileH As Long, _ByVal resourceId As Long, _ByVal suggestedFileName As String, _ByVal filterString As String, _ByVal defaultDirectory As String, _ByVal titleString As String) As Long

Private m_bCancelEnter As Boolean

Private m_flags As Long

Private m_FilterIndex As Long

Private m_Filter As String

Private m_FileName As String

Private m_iAction As Integer

Property Let Flags(lFlags As Long)

End Property

Property Get Flags() As Long

End Property

Public Property Get FilterIndex() As Long

FilterIndex = m_FilterIndex

End Property

Public Property Let FilterIndex(ByVal lFilterIndex As Long)

m_FilterIndex = lFilterIndex

End Property

Public Property Get Filter() As String

Filter = m_Filter

End Property

Public Property Let Filter(ByVal strFilter As String)

m_Filter = strFilter

End Property

Dec-09 53 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated

Calling C and MDL Functions

Public Property Get FileName() As String

FileName = m_FileName

End Property

Public Property Let FileName(ByVal strFileName As String)

TruncateAtEOS (strFileName)

m_FileName = strFileName

End Property

' Truncates a buffer to just contain the string that

' the C function returned.

Function TruncateAtEOS(str As String) As String

If str <> "" Then' This test avoids exception on zero-length string

TruncateAtEOS = Left$(str, InStr(1, str, vbNullChar) - 1)

End If

End Function

Public Sub ShowOpen()

Dim strNewFile As String

strNewFile = Space(1024)

If mdlDialog_fileOpen(strNewFile, 0, 0, "", m_Filter, _ "", "Select file") = 0 Then

TruncateAtEOS (strNewFile)

m_FileName = strNewFile

End If

End Sub

Public Property Get CancelEnter() As Boolean

CancelEnter = m_bCancelEnter

End Property

Public Property Let CancelEnter(ByVal bCancelEnter As Boolean)

m_bCancelEnter = bCancelEnter

End Property

Public Property Get Action() As Integer

Action = m_iAction

Extending Functionality 54 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Module Review

End Property

Public Property Let Action(ByVal iAction As Integer)

m_iAction = iAction

End Property

4 Run the macro.

To extend VBA to cover more possibilities, application developers need to be familiar with using other APIs that are complementary to VBA. 

Module Review

Now that you have completed this module, let’s measure what you have learned.

Questions

1 What is the convention for calling C functions from VBA?

2 How can an application access MDL functions and data?

3 Where can a developer find the MDL function declarations needed for VBA?

Dec-09 55 Extending FunctionalityCopyright © 2009 Bentley Systems, Incorporated

Module Review

Extending Functionality 56 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Events

Module Overview

Often applications will need to respond to some activity in the host environment. This is often the case in MicroStation, since it is an event machine. It constantly sends out messages that something is happening. 

In order for a macro to take advantage of this environment, the macro needs to set up methods to be called when certain events happen.

Module Prerequisites

• Understand the concept of events and event‐driven applications.

• Ability to create macros implementing commands that add elements to a model.

• Ability to create macros implementing commands that interactively find and query or modify elements in a model.

Refer to the Course Overview for additional pre‐requisites.

Module Objectives

After completing this module, you will be able to:

• Create VBA code to listen for and act upon design file open and close operations.

• Create VBA code to listen for and act upon events exposed through the implementation of interfaces.

Dec-09 57 Events

Copyright © 2009 Bentley Systems, Incorporated

Introductory Knowledge

Introductory Knowledge

Before you begin this module, let's define what you already know.

Questions

1 What is the purpose of using the IPrimitiveCommandEvents_Dynamics method in a Primitive command?

2 Which of the following is not a method of a class that implements the ILocateCommandEvents interface?

• ILocateCommandEvents_LocateFilter

• ILocateCommandEvents_Dynamics

• ILocateCommandEvents_DataPoint

• ILocateCommandEvents_Accept 

• ILocateCommandEvents_LocateFailed

• ILocateCommandEvents_Start

3 In a Locate command, what ancillary processing might the implemented interface class perform in its ILocateCommandEvents_LocateReset method?

Answers

1 IPrimitiveCommandEvents_Dynamics is used to let the command show reaction to mouse movements.

2 ILocateCommandEvents_DataPoint

3 Release some of the resources that have accumulated during the locate process.

Types of Events

There are two ways that events are caught in VBA. One is the WithEvents implementation. The other is by implementing interfaces and adding the class to the list of things to be called when an event occurs. 

Events 58 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Types of Events

The two events that MicroStation supports as part of the application are OnDesignFileOpened and OnDesignFileClosed. The rest are available by adding a class that has the proper interface and method signatures to listen for the events. You will see a simple macro that will handle both situations.

File events

Macros often use MicroStation file open and close events to manage external references that are file dependent. To set a macro to respond to the OnDesignFileOpened and OnDesignFileClosed events, the class needs to declare a variable WithEvents oVariableName As Application. This will register the class that has this variable to be called when it is loaded and a file is opened or closed.

Private Sub oApplication_OnDesignFileClosed(ByVal DesignFileName _As String)

Debug.Print "application on design file close event"

End Sub

Private Sub oApplication_OnDesignFileOpened(ByVal DesignFileName _As String)

Debug.Print "application on design file opened event"

End Sub

Interfaces

To provide a framework for macros to integrate with MicroStation’s normal operations, Bentley has built a set of interfaces that classes can implement. When a class implements an interface, and so is registered with MicroStation, the methods that are defined by the interface will be called in response to certain events. This makes MicroStation extensible for most any purpose.

For the vast majority of events in MicroStation, special event handlers will need to be added to the system. To do this, the class must implement the specific interface that is designed for the task. When a class implements an interface it will provide methods of a pre‐defined signature that MicroStation can safely call. 

As an example, in a class module add the line:

Implements IchangeTrackEvents

Dec-09 59 EventsCopyright © 2009 Bentley Systems, Incorporated

Types of Events

Now, in the class, add the calls that are required by the interface to be implemented:

IChangeTrackEvents_BeginUndoRedo – The undo/redo process is starting

IChangeTrackEvents_ElementChanged – an Element has changed

IChangeTrackEvents_FinishUndoRedo – The undo/redo process is completing

IChangeTrackEvents_Mark  – Mark the file to group a set of changes

To establish these methods, the class needs to be listed with the system to be active. To make the system aware, there should be a method on the class that represents the system, such as addXXX. This method will take a reference to a class as a parameter. This class will then be called for the appropriate event. 

It may be desirable to use the OnProjectLoad method to fire off the routine that will register the event listener. There is a parallel method for the unload event OnProjectUnload that is called when a project is removed.

Option Explicit

Private oEventHandlers As EventHandlerCls

Sub InstallEventHandlerCls()

RemoveEventHandlerCls

Set oEventHandlers = New EventHandlerCls

AddAttachmentEventsHandler oEventHandlers

AddChangeTrackEventsHandler oEventHandlers

AddLevelChangeEventsHandler oEventHandlers

AddModelActivateEventsHandler oEventHandlers

AddSaveAsEventsHandler oEventHandlers

AddViewUpdateEventsHandler oEventHandlers

End Sub

Sub RemoveEventHandlerCls()

If Not oEventHandlers Is Nothing Then

RemoveAttachmentEventsHandler oEventHandlers

RemoveChangeTrackEventsHandler oEventHandlers

RemoveLevelChangeEventsHandler oEventHandlers

RemoveModelActivateEventsHandler oEventHandlers

Events 60 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Types of Events

RemoveSaveAsEventsHandler oEventHandlers

RemoveViewUpdateEventsHandler oEventHandlers

End If

Set oEventHandlers = Nothing

End Sub

Sub OnProjectLoad()

InstallEventHandlerCls

End Sub

Sub OnProjectUnload()

RemoveEventHandlerCls

End Sub

The following interface classes are available:

• IAttachmentEvents — Lets macros monitor all of the changes to reference file attachments.

• IBatchConverterEvents — Lets macros monitor events related to MicroStation’s Batch Convertor utility.

• IChangeTrackEvents — Lets macros monitor all of the changes that happen to DesignFiles during a MicroStation session.

• IEnterIdleEvent — Lets macros receive notification that MicroStation has entered an idle state.

• ILevelChangeEvents — Defines the methods MicroStation uses to notify a macro of level‐change events.

• ILocateCommandEvents — Lets macros define element location commands. This is covered in the module “Building Commands”.

• IModalDialogEvents — Defines the methods MicroStation uses to notify a macro when the user opens and closes modal dialogs.

• IModelActivateEvents — Defines the methods MicroStation uses to notify a macro of model‐activate events.

• IModelChangeEvents — Defines the methods MicroStation uses to notify a macro of model‐change events.

• IPrimitiveCommandEvents — Lets macros define element placement commands. This is covered in the module “Building Commands”.

• IRasterEvents — Lets macros monitor all actions applied to rasters.

Dec-09 61 EventsCopyright © 2009 Bentley Systems, Incorporated

Module Review

• ISaveAsEvents — Lets macros provide methods that MicroStation calls both before and after it it performs any remapping that is required as part of a SaveAs command.

• IStandardsChecker — Lets macros extend MicroStation’s Standards Checker utility. This is covered in the module “Standards Checker Extensions”.

• IViewUpdateEvents — Defines the methods MicroStation uses to notify a macro of View update events. 

Exercise: Handle an event

1 In the VBA Project Manager dialog, create and load the project Running_Element_Count.

2 Switch to the Visual Basic Editor, insert a class module, and implement a class that will update the element count when a write to file is made.

3 Insert a class module and implement a class that will reset the element count when the model changes.

Module Review

Now that you have completed this module, let’s measure what you have learned.

Questions

1 Name at least five interfaces available to a MicroStation VBA macro?

2 True or False: When a class implements an interface it defines unique calling signatures for its methods.

3 How can an application work with raster events?

Events 62 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Standards Checker Extensions

Module Overview

The Standards Checker in MicroStation is an extensible utility enabling the validation of design file data against a customizable set of standards. Implementing a simple StandardsChecker extension is a matter of implementing the IstandardsChecker interface, then having a module that will register the class as a StandardsCheckerApplication. 

Module Prerequisites

• Understand the purpose of the Standards Checker utility in MicroStation.

• Familiar with the operation of the Standards Checker utility.

• Ability to create macros that listen for and act upon events.

Refer to the Course Overview for additional pre‐requisites.

Module Objectives

After completing this module, you will be able to:

• Develop custom plug‐ins to extend the functionality of the Standards Checker utility.

Introductory Knowledge

Before you begin this module, let's define what you already know.

Dec-09 63 Standards Checker Extensions

Copyright © 2009 Bentley Systems, Incorporated

Implementation

Questions

1 Which events does MicroStation support as part of the application?

2 What functionality does the AttachmentEvents interface class provide?

3 Which interface class defines the methods MicroStation uses to notify a macro of level‐change events?

Answers

1 OnDesignFileOpened and OnDesignFileClosed

2 The AttachmentEvents class lets VBA macros monitor changes to reference file attachments.

3 LevelChangeEvents

Implementation

The keys to implementing a StandardsChecker extension are to register the class only once to set up the settings and determine whether the user can interact with the check process. Standards Checker extensions can be implemented in any COM language, although only VBA macros are supported in the Power applications such as PowerDraft.

To implement a module for loading the class the code first has to make sure that there is not an already loaded class and if so unload that before loading the new one. To do this use the following structure for the module.

Public stdCheckerApp As clsStandardsCheckerImpl

Sub OnProjectLoad()

AddStdCheckerApp

End Sub

' There is nothing to prevent this program from adding 2 standards

‘ checkers. To prevent that from happening, call RemoveAttachmentsChecker

‘ before adding the checker.

Sub AddStdCheckerApp()

Standards Checker Extensions 64 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Implementation

RemoveStdCheckerApp

Set oSettingsCollection = New SettingCollection

oSettingsCollection.init

Set stdCheckerApp = New clsStandardsCheckerImpl

StandardsCheckerController.AddStandardsChecker stdCheckerApp, 1000

End Sub

Sub RemoveStdCheckerApp()

If Not stdCheckerApp Is Nothing Then

StandardsCheckerController.RemoveStandardsChecker stdCheckerApp

Set stdCheckerApp = Nothing

End Sub

Once this module is written, add the .mvba file to the list of Standards Checker applications by setting the configuration variable MS_STDCHECKERAPPS:

MS_STDCHECKERAPPS >  mystdchecker.mvba.

Simple – use the basic framework

A class needs to implement the IStandardsChecker interface to use the basic framework. When implementing this interface, the class will only have a stub for the GetFixDetail method. The GetFixDetail method will be explored in more detail later.

The IdentityString, DialogString, VersionString and Description properties are used by the user interface and reporting systems for the Standards Checker. These will allow the macro to show its name and description and to be involved in the reporting process. 

Not every checker macro will need to have some type of settings. By returning True for the HasSettings property, the settings button will be added to the standard user interface. The class needs to return True for the FoundSettings property if it is capable of operating. If the macro needs to have some settings before it can operate, then it needs to return False for FoundSettings. Any macro that returns False for the FoundSettings property is automatically disabled.

Dec-09 65 Standards Checker ExtensionsCopyright © 2009 Bentley Systems, Incorporated

Building Custom Standards Checker Applications

Advanced – use the Fixes optional dialog

The next step in the development of Standards Checker extensions is to add the capability to fix issues as they are found during the check process. To do this, the checker macro needs to call the ShowCheckerErrorWithFixOptions method which will call the GetFixDetails method.

This method requires the macro to fill a list of possible corrections for the user to pick from. These “fixes” are then presented to the user in the Standards Checker user interface. When the user has selected one of the fixes, it is sent back to the macro which then applies the fix to the element. Once the GetFixDetails method has returned the value of the ShowCheckerErrorWithFixOptions method, it is checked and one of the possible actions is taken. The results are then updated in the report using the StandardsCheckerReport class.

Building Custom Standards Checker Applications

One thing that is not apparent about the Standards Checker is the ability to extend the functionality to check more than just symbology or level definitions. It is a framework for processing design files. Application developers can use it to build custom plug‐in solutions to extend the capabilities. 

An example of this functionality is the ability to read a corporate standard in a common file that is read at check time. Since symbology is more complex, only one type of information can be on one level. Custom interpretation of the information is required.

To build a custom plug‐in, start by creating a new project. In this project, add a new module. This module will be the entry point for the macro. 

The next step is to insert a new class. In the new class, add the line to implement the IstandardsChecker interface. 

The next step is to simply add the required method and property signatures for the interface. Now that the class to handle the standard checking is in place, add a subroutine in the module to load the class into the standard checker. An important note is that the plug‐in can be loaded multiple times. To prevent this, the macro should unload the class before trying to load the class. To move the custom plug‐in to the top of the list, use a high priority number (the second parameter).

Standards Checker Extensions 66 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Building Custom Standards Checker Applications

Option Explicit

Public oSettingsCollection As SettingCollection

Public stdCheckerApp As clsStandardsCheckerImpl

Private oSC As IstandardsChecker

Sub OnProjectLoad()

AddStdCheckerApp

End Sub

Sub AddStdCheckerApp()

' There is nothing to prevent this program from adding 2 standards

‘ checkers. To prevent that from happening, call RemoveAttachmentsChecker

‘ before adding the checker.

RemoveStdCheckerApp

Set oSettingsCollection = New SettingCollection

oSettingsCollection.init

Set stdCheckerApp = New clsStandardsCheckerImpl

StandardsCheckerController.AddStandardsChecker stdCheckerApp, 1000

End Sub

Sub RemoveStdCheckerApp()

If Not stdCheckerApp Is Nothing Then

StandardsCheckerController.RemoveStandardsChecker stdCheckerApp

Set stdCheckerApp = Nothing

End Sub

By implementing the interface, the class code will appear as follows.

Implements IStandardsChecker

Private Property Get IStandardsChecker_CallForEachModel() As Boolean

End Property

Private Sub IStandardsChecker_CreateSettings()

End Sub

Dec-09 67 Standards Checker ExtensionsCopyright © 2009 Bentley Systems, Incorporated

Building Custom Standards Checker Applications

Private Sub IStandardsChecker_DeleteSettings()

End Sub

Private Property Get IStandardsChecker_Description() As String

End Property

Private Property Get IStandardsChecker_DialogString() As String

End Property

Private Sub IStandardsChecker_EditSettings(ByVal IsReadOnly As Boolean)

End Sub

Private Property Get IStandardsChecker_FoundSettings() As Boolean

End Property

Private Sub IStandardsChecker_GetFixDetail(Fixes() As String, _ByVal SelectedFix As Long, _FixPropertiesLabel As String, _FixProperties() As String)

End Sub

Private Property Get IStandardsChecker_HasSettings() As Boolean

End Property

Private Property Get IStandardsChecker_IdentityString() As String

End Property

Private Sub IStandardsChecker_RunCheck _(ByVal ModelToCheck As ModelReference, _ByVal FirstModel As Boolean, _ByVal Options As Long)

End Sub

Private Property Get IStandardsChecker_VersionString() As String

End Property

Standards Checker Extensions 68 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Building Custom Standards Checker Applications

The macro control properties and methods to note here are HasSettings, EditSettings, FoundSetting, CreateSetting, and DeleteSetting. The HasSettings method is called by the interface to determine if the macro has some configurable settings. This will activate the settings button on the user interface. When the user selects the settings button, the EditSettings method in the plug‐in will be invoked. Plug‐in settings are stored as a settings element in the DGN library. If the plug‐in wants to store its settings in the design file, the CreateSettings method is invoked by the standards macro. 

The DeleteSettings method is called to let the plug‐in delete the settings element. The FoundSettings property is used to tell the macro that the plug‐in has settings and is capable of running. For a macro to work properly, it must set FoundSettings to True so that it will be enabled on the user interface. Another property that a plug‐in should set is CallForEachModel. This tells the macro that the plug‐in should be called for each model in the design file.

The RunCheck method is called when the macro is invoked to check the elements. When the macro invokes the RunCheck method it will pass in the model that is being checked, some additional information such as if it is the first (or active) model, and some options that affect the operation of the plug‐in. In the RunCheck the plug‐in will do most of its work. The plug‐in will need to iterate through the elements it will check and, if necessary, invoke the user interface. To invoke the macro user interface, the plug‐in will call the StandardsCheckerController method, ShowCheckerError method, or ShowCheckerErrorWithFixOptions to interact with the user. 

ShowCheckerErrorWithFixOptions requires some set up by the macro. The plug‐in will provide the options that the user can choose from if they want to fix the element. The plug‐in needs to build a set of “fixes” that will be displayed to the user in the StandardsChecker user interface. Along with this array of information, the plug‐in will provide the descriptions and labeling for the user interface. 

When the user has responded to the prompting, the plug‐in needs to set the properties for the TotalProblems and FixedProblems that will be used in the user interface. If the plug‐in wants to add information to the XML report, it uses the StandardCheckerReport.AddProblem method. The last parameter for this method lets the report know if the plug‐in has fixed the element. The RunCheck is the heart of the plug‐in and, as such, will be the method that requires the most work when implementing a plug‐in.

The other methods provide some information about the macro to the standard interface. The Overstraining property lets the macro add information about the 

Dec-09 69 Standards Checker ExtensionsCopyright © 2009 Bentley Systems, Incorporated

Building Custom Standards Checker Applications

plug‐ins that are used in processing the file to the report. The IdentityString property is used to provide a unique name for the plug‐in. The DialogString property is applied to the main dialog that the user will select from in the macro. The Description property is used in report generation from the macro.

To demonstrate this process, this sample macro will use the RunCheck method to process each element in the file. The settings for this plug‐in will determine the behavior of the plug‐in. The plug‐in will use a collection of rules to determine if the element is correct. The collection of rules can be thought of as the standard that the plug‐in is checking. In the plug‐in, the standard is stored in a CSV file that is opened when the Settings button is pressed on the StandardsChecker user interface. When the CSV file is opened, the plug‐in will parse the file and build its collection of rules to apply. As each model is passed into the RunCheck method, the plug‐in will iterate through the elements of that model.

Private Sub IStandardsChecker_RunCheck _(ByVal ModelToCheck As ModelReference, _ByVal FirstModel As Boolean, _ByVal Options As Long)

Dim oEnum As ElementEnumerator

Dim oCheckElement As Element

If oSettingsCollection.GetSettings.Count = 0 Then

MsgBox "Rerun and Pick Settings file first"

Exit Sub

End If

Set oEnum = ModelToCheck.Scan

Do While oEnum.MoveNext

checkSingleElement oEnum.Current

Loop

End Sub

Each element is passed into the CheckSingleElement method that is defined in the plug‐in. The CheckSingleElement method will examine the element and, in the case of a simple element, it will apply the check to the element. If the element is complex it will get the components and check them recursively. If the element passes the check, it allows the StandardsChecker to continue processing.

Standards Checker Extensions 70 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Building Custom Standards Checker Applications

Sub checkSingleElement(ocheckel As Element)

Dim oSubEnum As ElementEnumerator

Dim oSubEl As Element

Dim oSetting As Setting

If ocheckel.IsComplexElement Then

Set oSubEnum = ocheckel.AsComplexElement.GetSubElements

Do While oSubEnum.MoveNext

checkSingleElement oSubEnum.Current

Loop

Else

If ocheckel.IsGraphical And _ocheckel.Class = msdElementClassPrimary Then

Set oSetting = New Setting

oSetting.InitFromElement ocheckel

If oSettingsCollection.check(oSetting) = False Then

ReportBadElement ocheckel

End If

End If

End If

End Sub

If the element fails the check it will be passed to the ReportBadElement method that the plug‐in defines. The ReportBadElement method will invoke the StandardsCheckerShowErrorWithFixOptions method. 

To call this method, the plug‐in will build the fixes array from the possible combinations that the element uses. The plug‐in uses the GetFixes method to generate information about the possible fixes. 

' ShowCheckerErrorWithFixOptions calls this to get the data to display

' in the bottom section of the dialog. It also calls this everytime the

' user selects a different row in the list of fixes.

'

Private Sub IStandardsChecker_GetFixDetail _(Fixes() As String, _ByVal selectedFix As Long, _FixPropertiesLabel As String, _FixProperties() As String)

FixPropertiesLabel = "VBA Example Fix Properties"

Dec-09 71 Standards Checker ExtensionsCopyright © 2009 Bentley Systems, Incorporated

Building Custom Standards Checker Applications

Dim oSetting As Setting

Dim i As Integer

i = oSettingsCollection.GetSettings.Count

ReDim FixProperties(1 To i, 0 To 2)

For i = 1 To oSettingsCollection.GetSettings.Count

FixProperties(i, 0) = "Symbology Settings"

FixProperties(i, 1) = oSettingsCollection.GetSettings.Item(i).ToString

FixProperties(i, 2) = oSettingsCollection.GetSettings(i).description

Next i

End Sub

When the user selects from the options on the StandardsChecker macro interface, the selectedFix option is set to one of the available options. The plug‐in uses this value to determine the process path. The msdStandardsCheckerReplaceChoiceSkip is set when the plug‐in should simply skip the element. 

The msdStandardsCheckerReplaceChoiceMarkIgnored option lets the plug‐in mark the element as ignored and the report can have these added so the user can revisit the element. The msdStandardsCheckerReplaceChoiceFix option tells the plug‐in that the element can be fixed according to the one of the fixes choices that the user has selected. The plug‐in can then take the corrective action and use the AddProblem method to set the element as fixed. Once the element has been processed the plug‐in will increment the TotalProblems and continue processing.

Private Sub ReportBadElement(oelm As Element)

Dim scc As StandardsCheckerController

Dim rpt As StandardsCheckerReport

Dim response As MsdStandardsCheckerReplaceChoice

Dim strDescr As String

Dim opts As MsdStandardsCheckerReplaceOptions

Dim handlerChoice As MsdStandardsCheckerReplaceChoice

Dim selFix As Long

Dim columnLabels(0 To 1) As String

Dim Fixes() As String

Dim oCurrSetting As New Setting

Dim i As Integer

Standards Checker Extensions 72 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Building Custom Standards Checker Applications

Dim scp As StandardsCheckerProblem

Set currElement = oelm

oCurrSetting.InitFromElement oelm

'StandardsCheckerController.ShowCheckerStatus "Example Status Message"

columnLabels(0) = "Symbology Options"

columnLabels(1) = ""

i = oSettingsCollection.GetSettings.Count

'set up the list of possible fixes for this element

ReDim Fixes(0 To i, 0 To 1)

Fixes(0, 0) = oCurrSetting.ToString: Fixes(0, 1) = "Current Settings"

For i = 1 To oSettingsCollection.GetSettings.Count

Fixes(i, 0) = oSettingsCollection.GetSettings.Item(i).ToString

Fixes(i, 1) = _oSettingsCollection.GetSettings.Item(i).description

Next i

strDescr = "Bad Element: " & DLongToString(oelm.ID) & _ElmToInfoString(oelm)

Set scc = StandardsCheckerController

opts = msdStandardsCheckerReplaceOptionCanFix Or _msdStandardsCheckerReplaceOptionCanIgnore

scc.ShowCheckerErrorWithFixOptions response, selFix, _strDescr, _"Fixes List Box Label", _columnLabels, Fixes, 0, _opts, _False

Set rpt = scc.Report

If response = msdStandardsCheckerReplaceChoiceSkip Then

Set scp = rpt.AddProblem(strDescr, "My Check", False)

' Record the ElementID because it is the only property of an Attachment

Dec-09 73 Standards Checker ExtensionsCopyright © 2009 Bentley Systems, Incorporated

Building Custom Standards Checker Applications

‘ that cannot change. If someone writes a program that processes

' the problem report, they can use the ElementID to be certain the

' program accesses the same attachment that the report refers to.

scp.AddElementID stdCheckerApp.currElement.ID

scp.AddStandard "My Element Checker App", m_libraryID

scp.AddVariance "My Symbology", "", oCurrSetting.ToString

End If

If response = msdStandardsCheckerReplaceChoiceFix Then

Dim aSetting As Setting

Set scp = rpt.AddProblem(strDescr, "My Check", True)

'the last parameter will put the check in the fixed column

scp.AddStandard "My Element Checker App", m_libraryID

scp.AddAction "Fixed Element Symbology"

scp.AddElementID stdCheckerApp.currElement.ID

If selFix > 0 Then

Set aSetting = oSettingsCollection.GetSettings(selFix)

Set oelm.Level = _ActiveDesignFile.Levels.Find(aSetting.Level)

oelm.Color = aSetting.Color

oelm.LineWeight = aSetting.Weight

End If

oelm.Redraw

oelm.Rewrite

scc.FixedProblems = scc.FixedProblems + 1

End If

If response = msdStandardsCheckerReplaceChoiceMarkIgnored Then

Set scp = rpt.AddIgnoredProblem("Element ", strDescr, _"My Check", False)

scp.AddAction "Fixed Element Symbology"

scp.AddElementID stdCheckerApp.currElement.ID

scp.AddStandard "My Element Checker App", m_libraryID

scp.AddVariance "My Symbology", "", oCurrSetting.ToString

scc.IgnoredProblems = scc.IgnoredProblems + 1

End If

Standards Checker Extensions 74 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Building Custom Standards Checker Applications

' If the user entered Cancel, tell RunCheck to abort

If response = msdStandardsCheckerReplaceChoiceAbort Then

stdCheckerApp.m_aborted = True

scc.TotalProblems = scc.TotalProblems + 1

End Sub

The plug‐in can use the AddedCheckerToStandardsCheckerApps method to add information to the report and call the AddLibraryToCheckerApp to add a node to the report. The report’s AddStandardToLibrary method is used to add the information to the XML data.

Private Sub IStandardsChecker_AddedCheckerToStandardsCheckerApps _(ByVal ApplicationXMLNode As Object)

Dim rpt As StandardsCheckerReport

' If the program declares these as IXMLDOMNode, then the program

' can add custom XML data to the report. If the program declares them

' as IXMLDOMNode then it must also set a reference to Microsoft XML

' v4.0.

Dim oLibraryNode As Object ' or IXMLDOMNode

Set rpt = StandardsCheckerController.Report

' m_libraryID is output from AddLibraryToCheckerApp.

' It is used later as input to AddStandard

Set oLibraryNode = rpt.AddLibraryToCheckerApp(ApplicationXMLNode, _StandardsCheckerController.SettingsFile, m_libraryID)

rpt.AddStandardToLibrary oLibraryNode, "Element Checker", "Elements"

End Sub

The framework that is provided in the IStandardsChecker interface allows the plug‐in developer to interact with the StandardsChecker user interface and build a seamless extension. The class that implements the IstandardChecker interface does not need to be developed in VBA — it can be defined in any language that supports COM.

The StandardsChecker keeps a list of COM objects that it will invoke. The configuration variable MS_STANDARDCHECKER_APP can be used to automatically load the plug‐in. If the file name ends in .mvba, it will automatically be added to 

Dec-09 75 Standards Checker ExtensionsCopyright © 2009 Bentley Systems, Incorporated

Building Custom Standards Checker Applications

the list of applications that are loaded when the StandardsChecker is invoked by the user. There are many problems for plug‐ins to solve such as checking for missing references, etc.

Exercise: Implement a check

1 Implement the simple missing reference checker.

Questions

1 What are the keys to implementing a StandardsChecker extension? 

2 In an implementation of the IStandardsChecker interface, what is the purpose of calling the HasSettings method?

3 True or False: A class implementing an IStandardsChecker interface need not be developed in VBA — it can be defined in any language that supports COM.

Standards Checker Extensions 76 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Module Review Answers

Building Commands

Questions

1 What are events?

2 What are the events that are available to the IPrimitiveCommandEvents interface?

3 What are the events that are available to the ILocateCommandEvents interface?

4 How does one make use of an interface?

Answers

1 Events are triggered as the user interacts with various parts of MicroStation.

2 CleanupDataPoint

Dynamics

Keyin

Reset

Start

Dec-09 77 Module Review Answers

Copyright © 2009 Bentley Systems, Incorporated

User Interface

3 Accept

Cleanup

Dynamics

LocateFailed

LocateFilter

LocateReset

Start

4 Create a class module that implements the interface. Use the Implements keyword in the class module and specify the interface object — for example, Implements IPrimitiveCommandEvents.

User Interface

Questions

1 Name at least five standard user interface controls available to VBA developers?

2 How can an Image Control be used in a dialog?

3 What methods must be included to make a form resizable?

Answers

1 CheckBox

ComboBox

CommandButton

Frame

Image

ListBox

MultiPage

OptionButton

ScrollBar

Module Review Answers 78 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Working With Non‐Graphic Data

SpinButton

ToggleButton

TabStrip

2 The SpinButton and ScrollBar can be used for manipulating the image that is displayed in the Image control. 

3 FindWindow

GetWindowLong

SetWindowLong

Working With Non‐Graphic Data

Questions

1 What is the preferred method to work with an external database?

2 What is required to work with User Data?

3 What tools are used to work with XML data?

Answers

1 DSN data source

2 User attribute ID assigned by Bentley

3 MSXML

Extending Functionality

Questions

1 What is the convention for calling C functions from VBA?

2 How can an application access MDL functions and data?

3 Where can a developer find the MDL function declarations needed for VBA?

Dec-09 79 Module Review AnswersCopyright © 2009 Bentley Systems, Incorporated

Events

Answers

1 The GetCExpressionValue method takes in the “C” expression and, optionally, the MDL application that is required.  The SetCExpressionValue method takes in the expression used to set some value in an MDL application.

2 Import functions from other applicable libraries provided with MicroStation by using the Declare statement and providing the prototype for the function call in a Visual Basic module.

3 MDL Function Reference help document (MDLAPIFunctionReference.chm) in the MicroStation SDK

Events

Questions

1 Name at least five interfaces available to a MicroStation VBA macro?

2 True or False: When a class implements an interface it defines unique calling signatures for its methods.

3 How can an application work with raster events?

Answers

1 IAttachmentEvents

IBatchConverterEvents

IChangeTrackEvents

IEnterIdleEvent

ILevelChangeEvents

ILocateCommandEvents

IModalDialogEvents

IModelActivateEvents

IModelChangeEvents

IPrimitiveCommandEvents

Module Review Answers 80 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Standards Checker Extensions

IRasterEvents

ISaveAsEvents

IStandardsChecker

IViewUpdateEvents

2 False. The class must provide methods of a pre‐defined signature that MicroStation can safely call.

3 Create a class module that implements an IRasterEvents interface.

Standards Checker Extensions

Questions

1 What are the keys to implementing a StandardsChecker extension? 

2 In an implementation of the IStandardsChecker interface, what is the purpose of calling the HasSettings method?

3 True or False: A class implementing an IStandardsChecker interface need not be developed in VBA — it can be defined in any language that supports COM.

Answers

1 Register the class only once to set up the settings and determine whether the user can interact with the check process.

2 The HasSettings method is called to determine if the macro has some configurable settings.

3 True.

Dec-09 81 Module Review AnswersCopyright © 2009 Bentley Systems, Incorporated

Standards Checker Extensions

Module Review Answers 82 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Appendix

New to MicroStation VBA?

Visual Basic for Applications in MicroStation is Bentley’s implementation of the standard VBA SDK, with extensions that are relevant to the MicroStation environment. Visual Basic and its sibling, Visual Basic for Applications (VBA), are modern, Object‐oriented programming languages that are for use by professional and casual application developers alike. VBA in MicroStation provides an environment that has previously only been available to MDL/C programmers.   VBA applications can integrate into the standard workflow of MicroStation applications.

The MicroStationDGN Object Model

In order to make the best use of the programming tools, a basic understanding of the application and its file structure is necessary. 

The Application object represents the running instance of MicroStation. This means that all the features found in the product are accessible to VBA macros. A review of the Application object shows some important properties. Among these properties are the ActiveDesignFile, ActiveModelReference, and ActiveSettings. These represent the current design file, the active model, and the current settings. 

Some other objects that are important to understand are Attachment, DesignFile, Element, and ModelReference. 

• The Attachment object is a model attached as a reference. 

• The DesignFile object is the container that holds models. 

• The Element object is the base for all elements in the model, these include visible and non‐visible elements and simple and complex elements. 

• The ModelReference object is the container for all the elements. 

Dec-09 83 Appendix

Copyright © 2009 Bentley Systems, Incorporated

System Overview

There can be multiple models in one design file, but only one model is active at a time. The ModelReference will have Element caches for the graphical elements, and the DesignFile will have caches for the non‐model and control elements. The sub‐classes of elements match the types of elements that are in the design file.

In the Application Object there are many methods that can be useful when developing macros that are computationally complex. Point3d can be used to do vector math, and the Matrix3d and Transform groups of methods are used to scale and manipulate elements.

In the MicroStation Visual Basic for Applications help document, the Objects topic provides properties, methods and examples for many of the MicroStation objects.

System Overview

Configuration Variables for VBA

MS_VBANEWPROJECTDIRECTORY determines the default location for new Visual Basic project files.

MS_VBASEARCHDIRECTORIES determines the directories in which MicroStation will search for projects when they are loaded by name.

Appendix 84 Dec-09Copyright © 2009 Bentley Systems, Incorporated

System Overview

MS_VBAAUTOLOADPROJECTS and MS_VBAREQUIREDPROJECTS determine which projects are automatically loaded at startup time.

MS_VBASAVEONRUN determines whether modified VBA projects are automatically saved when a VBA macro is executed.

MS_VBA_OPEN_IN_MEMORY controls whether MicroStation keeps a project's data available by keeping the project's disk file open, or by copying the project into memory.

MS_VBAGUIDREFERENCES identifies DLLs and type libraries to which references will be automatically added in newly created projects.

MS_VBANAMEDREFERENCES identifies projects to which references will be automatically added in newly created projects.

MS_STANDARDSCHECKER_APPS holds a list of MDL applications.  If the name of the file has .MVBA as the file extension the VBA project will be loaded.

Keyins for VBA

Following is a list of MicroStation VBA key‐ins.

• VBA CREATE <project name> — creates and loads a project with the specified name. The project file will be created with the specified project name as the root name and the extension .mvba. The file will be located in the directory specified by the configuration variable MS_VBANEWPROJECTDIRECTORY. If that configuration variable is not defined and a path is not specified in the key‐in, the file will be located in ...\Bentley\Program\MicroStation\.

• VBA EDIT — switches to the Visual Basic Editor. 

• VBA EDIT <project name> — opens the specified project in the Visual Basic Editor. If the project is not currently loaded but can be found in the path(s) specified by the configration variable MS_VBASEARCHDIRECTORIES, it will be loaded automatically. 

• VBA EXECUTE [project name] <VBA statement> — executes the specified VBA statement in the context of the specified project. If a project name is not specified, MicroStation chooses a project to use as the context. There is no need to specify a context if the statement uses only the MicroStationDGN object model. The statement is not case‐sensitive.

Example:VBA EXECUTE If ActiveModelReference.Is3D Then MsgBox "Active model is 3D."

Dec-09 85 AppendixCopyright © 2009 Bentley Systems, Incorporated

COM Client Applications

• VBA HIDE — exits the Visual Basic Editor.

• VBA LOAD — opens the Load Project dialog.

• VBA LOAD <project name> — loads the specified project. If the file path to the .mvba file is not specified, then MicroStation searches in the directories specified by the configuration variable MS_VBASEARCHDIRECTORIES. If a .mvba file is found with a root name that matches the specified project name, it is loaded. 

• VBA PAUSE RECORDING — pauses the recording of a macro.

• VBA RESUME RECORDING — resumes recording of a previously paused macro.

• VBA RUN — opens the Macros dialog.

• VBA RUN <macro name>— runs the specified macro.

• VBA RUN [project name]<macro name> — loads the specified project if it is not already loaded and runs the specified macro. You need to include the square bracket delimiters [ ] with the project name.

• VBA SAVE <project name> — saves the specified project.

• VBA SAVEALL — saves all loaded projects.

• VBA SHOW EDITOR — switches to the Visual Basic Editor.

• VBA SHOW MACROS — opens the Macros dialog.

• VBA START RECORDING — starts the recording of a macro.

• VBA STOP RECORDING — stops the recording of a macro.

• VBA UNLOAD <project name> — unloads the specified project.

COM Client Applications

For proprietary MicroStation customizations where the code must be hidden from the user, developers can build compiled COM client applications (.exe). MicroStation exposes a type library for use by COM clients in accessing MicroStation functionality. The basis of this type library is the MicroStationDGN Object Model.

Microsoft’s Visual Basic (VB) development tools — VB6 and VB.NET — can be used to build COM client applications for MicroStation.

When building a COM client application, add a reference to the MicroStationDGN object, then set a variable to the MicroStationDGN 

Appendix 86 Dec-09Copyright © 2009 Bentley Systems, Incorporated

ActiveX Controls and DLLs

Application. You can then access all the functionality of the MicroStationDGN Object Model.

At run time, a COM client application attaches to the most recently launched instance of MicroStation. If MicroStation is not running, the default MicroStation will start. This has some drawbacks if you use a special workspace or configuration.

ActiveX Controls and DLLs

ActiveX is a framework for defining reusable software components that perform a particular function or a set of functions in Microsoft Windows in a way that is independent of the programming language used to implement them.

• An ActiveX Control is a compiled .ocx file that implements user interface controls more sophisticated than those available in VBA. An example would be a TreeView control.

• An ActiveX DLL is a compiled .dll file that isolates specific logic and calculations from general application logic in a VBA macro or VB application.

Like standalone COM client applications (.exe) for MicroStation, ActiveX Controls and DLLs for MicroStation rely on the type library exposed by MicroStation. Microsoft’s Visual Basic development tools, v6.0 or greater, can be used to build ActiveX Controls and DLLs. (VB.NET does not support development of ActiveX Controls and DLLs.)

After you build an ActiveX Control or ActiveX DLL you need to register it using the Windows command regsevr32. To incorporate an ActiveX Object or DLL in a VBA macro, you need to reference the DLL — select Tools > References in the Visual Basic Editor. After you do so, you can write VBA code that accesses the ActiveX Object or DLL.

ActiveX Objects and DLLs can even include references to MicroStationDGN objects. A simple example of an ActiveX DLL that includes such a reference is as follows.

This function is in a VB project, MarksMath, that has a class, Simple, which contains this code.

Public Function GetCurrentDesignFileName() As String

Dim oMSApp As MicroStationDGN.Application

Set oMSApp = MicroStationDGN.Application

Dec-09 87 AppendixCopyright © 2009 Bentley Systems, Incorporated

Interface Programming

GetCurrentDesignFileName = oMSApp.ActiveDesignFile.Name

End Function

The VBA code looks like this:

Sub workwithmarksmath()

Dim oMath As MarksMath.Simple

Set oMath = New MarksMath.Simple

Dim oMathApp As MarksMath.Application

Set oMathApp = New MarksMath.Application

Dim oString As StringoString = oMath.GetCurrentDesignFileName

Debug.Print oString

End Sub

Interface Programming

An Interface is a group of properties, events, and methods that belong to a class. The class that is implementing the interface must provide an implementation of each of the properties, events, and methods that are specified by the Interface.

Sample Macros

Scanning

Sub SimpleScan()

Dim oScanCriteria As ElementScanCriteria

Dim oElementEnum As ElementEnumerator

Dim i As Long

i = 0

Set oScanCriteria = New ElementScanCriteria

oScanCriteria.IncludeOnlyVisible

Set oElementEnum = ActiveModelReference.Scan(oScanCriteria)

Do While oElementEnum.MoveNext

Debug.Print "Found an Element of type Number " & _oElementEnum.Current.Type

Appendix 88 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Sample Macros

i = i + 1

Loop

Debug.Print "found a total of " & i & "elements"

End Sub

Geometry

Private Function computeCenterPoint(oEl As LineElement) As Point3d

computeCenterPoint = Point3dInterpolate(oEl.Range.Low, 0.5, _oEl.Range.High)

End Function

Private Function ComputeLow(ept As Point3d, npt As Point3d) As Point3d

Dim tPt As Point3d

tPt = ept

If npt.x < ept.x Then

tPt.x = npt.x

End If

If npt.Y < ept.Y Then

tPt.Y = npt.Y

End If

If npt.Z < ept.Z Then

tPt.Z = npt.Z

End If

ComputeLow = tPt

End Function

Private Function ComputeHigh(ept As Point3d, npt As Point3d) As Point3d

Dim tPt As Point3d

tPt = ept

If npt.x > ept.x Then

tPt.x = npt.x

End If

Dec-09 89 AppendixCopyright © 2009 Bentley Systems, Incorporated

Sample Macros

If npt.Y > ept.Y Then

tPt.Y = npt.Y

End If

If npt.Z > ept.Z Then

tPt.Z = npt.Z

End If

ComputeHigh = tPt

End Function

Private Function ComputeMaxRange(pts() As Point3d) As Range3d

Dim oRange As Range3d

Dim i As Long

oRange.High = pts(0)

oRange.Low = pts(0)

For i = LBound(pts) To UBound(pts)

oRange.High = ComputeHigh(oRange.High, pts(i))

oRange.Low = ComputeLow(oRange.Low, pts(i))

Next

ComputeMaxRange = oRange

End Function

Sub CreateBoundryPoints(aPoints() As Point3d, _bPoints() As Point3d, _uOrder As Long, _vOrder As Long)

ReDim bPoints(uOrder * 2 + vOrder * 2)

Dim i As Long

For i = 0 To uOrder

bPoints(i) = Point3dInterpolate(aPoints(0), i / uOrder, _aPoints(1))

Next

Dim x As Long

x = 0

For i = uOrder To uOrder + vOrder

Appendix 90 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Sample Macros

bPoints(i) = Point3dInterpolate(aPoints(1), x / vOrder, _aPoints(2))

x = x + 1

Next

x = 0

For i = uOrder + vOrder To (uOrder * 2) + vOrder

bPoints(i) = Point3dInterpolate(aPoints(2), x / uOrder, _aPoints(3))

x = x + 1

Next

x = 0

For i = (uOrder * 2) + vOrder To (uOrder * 2) + (vOrder * 2)

bPoints(i) = Point3dInterpolate(aPoints(3), x / vOrder, _aPoints(0))

x = x + 1

Next

End Sub

Sub ConvertRangeToPoints(oRange As Range3d, pts() As Point3d)

pts(0) = oRange.Low

pts(2) = oRange.High

pts(1) = oRange.Low

pts(1).x = oRange.High.x

pts(3) = oRange.High

pts(3).x = oRange.Low.x

End Sub

Sub testDTM()

Dim oSurface As BsplineSurfaceElement, oElement As Element

Dim surface As BsplineSurface

Dim aFitPoints() As Point3d

Dim aNullWeights() As Double

Dim oScanCrit As New ElementScanCriteria

Dim oEnumerator As ElementEnumerator

Dim count As Long, i As Long

' get all text elements (assumed to be DTM atoms)

Dec-09 91 AppendixCopyright © 2009 Bentley Systems, Incorporated

Sample Macros

oScanCrit.ExcludeAllTypes

oScanCrit.IncludeType msdElementTypeLineString

Set oEnumerator = ActiveModelReference.Scan(oScanCrit)

'Count points

Do While oEnumerator.MoveNext

count = count + 1

Loop

ReDim aFitPoints(0 To count - 1)

oEnumerator.Reset

i = 0

Do While oEnumerator.MoveNext

Set oElement = oEnumerator.Current

With oElement.AsLineElement

aFitPoints(i) = computeCenterPoint(oElement)

oElement.Redraw msdDrawingModeHilite

i = i + 1

End With

Loop

Dim oRangeBlock As Range3d

oRangeBlock = ComputeMaxRange(aFitPoints)

Dim oShape As ShapeElement

Dim aVerts(3) As Point3d

ConvertRangeToPoints oRangeBlock, aVerts

Set oShape = CreateShapeElement1(Nothing, aVerts)

oShape.Redraw msdDrawingModeHilite

ActiveModelReference.AddElement oShape

Dim uOrder As Long

Dim vOrder As Long

Appendix 92 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Sample Macros

Dim boundryPoints() As Point3d

uOrder = 10

vOrder = 10

'copy the orgdata

Dim orgData() As Point3d

orgData = aFitPoints

'this function will create some boundry points to keep the surface 'from going nuts!

CreateBoundryPoints aVerts, boundryPoints, uOrder, vOrder

ReDim Preserve aFitPoints(0 To (count + (2 * uOrder) + _(2 * vOrder)) - 1)

Dim x As Long

Dim l As Long

l = 0

x = (count + (2 * uOrder) + (2 * vOrder)) - 1

For x = count To UBound(aFitPoints)

aFitPoints(x) = boundryPoints(l)

l = l + 1

Next

'Compute 10 x 10 LSQ cubic surface (5 minutes)

Set surface = New BsplineSurface

surface.FromWeightedPointsAndPlane aFitPoints, aNullWeights, _Transform3dZero, uOrder, vOrder, 4, 4

Set oSurface = CreateBsplineSurfaceElement1(Nothing, surface)

oSurface.Color = 3

ActiveModelReference.AddElement oSurface

oSurface.Redraw

'reset things?

aFitPoints = orgData

uOrder = 15

vOrder = 15

CreateBoundryPoints aVerts, boundryPoints, uOrder, vOrder

Dec-09 93 AppendixCopyright © 2009 Bentley Systems, Incorporated

Sample Macros

ReDim Preserve aFitPoints(0 To (count + (2 * uOrder) + _(2 * vOrder)) - 1)

l = 0

For x = count To UBound(aFitPoints)

aFitPoints(x) = boundryPoints(l)

l = l + 1

Next

'Compute 30 x 30 LSQ cubic surface (14 minutes)

surface.FromWeightedPointsAndPlane aFitPoints, aNullWeights, _Transform3dZero, uOrder, vOrder, 4, 4

Set oSurface = CreateBsplineSurfaceElement1(Nothing, surface)

oSurface.Color = 4

ActiveModelReference.AddElement oSurface

oSurface.Redraw

'Compute 30 x 30 LSQ quadratic surface (14 minutes)

'reset things?

aFitPoints = orgData

uOrder = 30

vOrder = 30

CreateBoundryPoints aVerts, boundryPoints, uOrder, vOrder

ReDim Preserve aFitPoints(0 To (count + (2 * uOrder) + _(2 * vOrder)) - 1)

l = 0

x = (count + (2 * uOrder) + (2 * vOrder)) - 1

For x = count To UBound(aFitPoints)

aFitPoints(x) = boundryPoints(l)

l = l + 1

Next

surface.FromWeightedPointsAndPlane aFitPoints, aNullWeights, _Transform3dZero, uOrder, vOrder, 3, 3

Appendix 94 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Sample Macros

Set oSurface = CreateBsplineSurfaceElement1(Nothing, surface)

oSurface.Color = 5

ActiveModelReference.AddElement oSurface

oSurface.Redraw

'Compute 50 x 50 LSQ quadratic surface (1 hr 45 min)

'reset things?

aFitPoints = orgData

uOrder = 50

vOrder = 50

CreateBoundryPoints aVerts, boundryPoints, uOrder, vOrder

ReDim Preserve aFitPoints(0 To (count + (2 * uOrder) + _(2 * vOrder)) - 1)

l = 0

x = (count + (2 * uOrder) + (2 * vOrder)) - 1

For x = count To UBound(aFitPoints)

aFitPoints(x) = boundryPoints(l)

l = l + 1

Next

surface.FromWeightedPointsAndPlane aFitPoints, aNullWeights, _Transform3dZero, uOrder, vOrder, 3, 3

Set oSurface = CreateBsplineSurfaceElement1(Nothing, surface)

oSurface.Color = 6

ActiveModelReference.AddElement oSurface

oSurface.Redraw

'Compute 50 x 75 LSQ quadratic surface

'reset things?

aFitPoints = orgData

uOrder = 50

vOrder = 75

CreateBoundryPoints aVerts, boundryPoints, uOrder, vOrder

Dec-09 95 AppendixCopyright © 2009 Bentley Systems, Incorporated

Sample Macros

ReDim Preserve aFitPoints(0 To (count + (2 * uOrder) + _(2 * vOrder)) - 1)

l = 0

x = (count + (2 * uOrder) + (2 * vOrder)) - 1

For x = count To UBound(aFitPoints)

aFitPoints(x) = boundryPoints(l)

l = l + 1

Next

surface.FromWeightedPointsAndPlane aFitPoints, aNullWeights, _Transform3dZero, uOrder, vOrder, 4, 4

Set oSurface = CreateBsplineSurfaceElement1(Nothing, surface)

oSurface.Color = 7

ActiveModelReference.AddElement oSurface

oSurface.Redraw

End Sub

Working with other files

Sub ScanAndCount()

Dim fileName As String

Dim oDesignFile As DesignFile

Dim oElementCache As ElementCache

Dim oEnum As ElementEnumerator

Dim oScanCriteria As ElementScanCriteria

Dim iSize As Long

Dim iCount As Long

Dim oModel As ModelReference

Dim i As Long

fileName = "d:\data\dwg\g1088-p4-2.dwg"

Set oDesignFile = OpenDesignFileForProgram(fileName, True)

For Each oModel In oDesignFile.Models

Appendix 96 Dec-09Copyright © 2009 Bentley Systems, Incorporated

Sample Macros

i = 0

Set oElementCache = oModel.GraphicalElementCache

iCount = oElementCache.Count

Set oScanCriteria = New ElementScanCriteria

Set oEnum = oElementCache.Scan(oScanCriteria)

Do While oEnum.MoveNext

Debug.Print "Found an Element of type Number " & _oEnum.Current.Type

i = i + 1

Loop

Debug.Print "Found " & i & "elements in this file " & _oModel.name

Next

oDesignFile.Close

End Sub

Dynamic user interface

Option Explicit

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _(ByVal lpClassName As String, _ByVal lpWindowName As Any) As Long

Private Declare Function GetWindowLong Lib "user32" Alias _"GetWindowLongA" _(ByVal HWND As Long, _ByVal nIndex As Long) As Long

Private Declare Function SetWindowLong Lib "user32" Alias _"SetWindowLongA" _(ByVal HWND As Long, _ByVal nIndex As Long, _ByVal dwNewLong As Long) As Long

Private Const WS_THICKFRAME = &H40000

Private Const GWL_STYLE = (-16)

Private Sub UserForm_Activate()

Dim lngFrmHWND As Long

Dim lngStyle As Long

Dec-09 97 AppendixCopyright © 2009 Bentley Systems, Incorporated

Sample Macros

Dim lngRetVal As Long

lngFrmHWND = FindWindow("ThunderDFrame", Me.Caption)

lngStyle = GetWindowLong(lngFrmHWND, GWL_STYLE)

lngRetVal = SetWindowLong(lngFrmHWND, GWL_STYLE, lngStyle Or _WS_THICKFRAME)

End Sub

Private Sub UserForm_Resize()

'ListBox2.Top = Me.Top

ListBox2.Left = 1

ListBox2.Width = Me.Width - 9

ListBox2.Height = Me.Height / 2 - 5

Label1.Top = ListBox2.Height + 2

Label1.Height = 5

Label1.Width = ListBox2.Width

ListBox1.Top = ListBox2.Height + Label1.Height + 5

'ListBox1.Height = Me.Height - (ListBox2.Height + Label1.Height + 18)

ListBox1.Height = Me.Height / 2 - 10

ListBox1.Left = ListBox2.Left

ListBox1.Width = ListBox2.Width

Debug.Print "Height: " & Me.Height & " Width:" & Me.Width

Debug.Print "the upper list box is "; ListBox2.Height; _" tall starts at "; ListBox2.Top

Debug.Print "the lower list box is "; ListBox1.Height; _" tall starts at "; ListBox1.Top

End Sub

Appendix 98 Dec-09Copyright © 2009 Bentley Systems, Incorporated