The MicroStation Development Library (MDL) provides an API for developers wanting to create custom applications for MicroStation® from Bentley Systems.

We usually create a MicroStation application as a DLL, written using C++ and built with the Microsoft C++ compiler and linker. You can choose whether to use Microsoft Visual Studio IDE or the Bentley Systems bmake tools.

Element Location & Scanning

We often want to locate a graphic element in a MicroStation DGN model. The locate operation may be interactive or non-interactive. That is, we want the user to locate an element, or we want to scan the model to find one or more elements.

As developers, we use the mdlLocate_api to write interactive locate logic (although this is superseded by classes provided by the MicroStationAPI). We use the mdlScanCriteria_api to write code that scans a model. In a given application, we often want to write code that does each of the above at different times: sometimes we want a user to locate an element, at other times we want to scan a model and do something with the result set.

Element Location: mdlLocate_api

MDL's locate logic requires us to set a locate mask prior to choosing elements. The locate mask restricts MicroStation's logic to allow only certain element types to be chosen. Here's an example that lets us locate lines, line-strings, and shapes …

static int  searchMask [] = {   LINE_ELM,
                                LINE_STRING_ELM,
                                SHAPE_ELM,
                            };
mdlLocate_setElemSearchMask (sizeof (searchMask) / sizeof (searchMask [0]), searchMask);

Element Scanning: mdlScanCriteria_api

MDL's scanning logic requires us to set scan criteria prior to searching for elements. Here's an example: pay particular attention to the lines that set the element type …

ScanCriteria*	pCriteria	= mdlScanCriteria_create ();
if (NULL != pCriteria
    &&
    SUCCESS == mdlScanCriteria_setModel (pCriteria, modelRef))
    {
        //   Set the scan criteria element type test
        UShort		typeMask	[6];
        memset (typeMask, 0, sizeof (typeMask));
        typeMask[0]		= TMSK0_LINE | TMSK0_LINE_STRING | TMSK0_SHAPE;
        mdlScanCriteria_setDrawnElements  (pCriteria);
        mdlScanCriteria_setElementTypeTest (pCriteria, typeMask, sizeof (typeMask));
        //	Perform the scan
        mdlScanCriteria_setReturnType (pCriteria, MSSCANCRIT_ITERATE_ELMDSCR, FALSE, TRUE);
        mdlScanCriteria_setElmDscrCallback (pCriteria, scan_callbackCreateShapeList, &args);
        mdlScanCriteria_setStopFilePos (pCriteria, mdlModelRef_getEof (modelRef));
        mdlScanCriteria_scan (pCriteria, NULL, NULL, NULL);
        mdlScanCriteria_free (pCriteria);
    }

Two APIs: Two Ways to Set Element Types

The two code snippets above illustrate that the logic required to set up element type testing is quite different, depending on whether you are locating or scanning for elements. Even the element type macros (LINE_ELM for locate, TMSK0_LINE for scanning) have different names.

The problem with two different APIs is that it's hard to remember to keep them synchronised. If we want to add the ability to locate ellipses, then we modify one section of code. But we probably want to scan for ellipses as well, so we must modify another unrelated section of code. This is poor programming practise, forced on us by the history of MDL and the C programming language.

C++ Class Unifies Location & Scanning

C++ class CSearchTypes hides the differences between MDL's location and scanning logic. The MDL code above is hidden inside the class's private member functions. You don't have to concern yourself with the different macros — you need only use the LINE_ELM macros. CSearchTypes is designed to be inherited, so you can easily create a custom class for exactly those element types that interest you. Here's an extract from the header file …

//	typedef clarifies subsequent code
typedef	std::vector<int>                        ElementTypeCollection;
typedef ElementTypeCollection::iterator         ElementTypeIterator;
typedef ElementTypeCollection::const_iterator   ElementTypeConstIterator;

class CSearchTypes
{
protected:
    ElementTypeCollection types_;

public:
    //////////////////////////////////////////////////////////////////
    //	Implementation
    UInt32              Count               () const { return static_cast(types_.size ()); };
    //	Set MicroStation's locate search mask
    void                SetLocateSearchMask ()	const;
    //	Set MicroStation's scan criteria type mask
    bool                SetScanCriteria     (ScanCriteria*          criteria) const;
    //	Is an element accepted by this search type
    bool                Acceptable          (int                    type)   const;
    virtual bool        Acceptable          (const ElementRef       el)     const;
    virtual bool        Acceptable          (MSElement const*       el)     const;
    virtual bool        Acceptable          (MSElementDescr const*  el)     const;
    //////////////////////////////////////////////////////////////////
    //	Construction
    CSearchTypes            ()  {};
    virtual ~CSearchTypes   ()  {};
protected:
    UInt32              CreateTypeMask      (UShort                 mask [],
                                            int                     size)   const;
};

You create your class that inherits from CSearchTypes for particular element types. Here's an example of a class specialised for linear types …

class LinearTypes	:	public CSearchTypes
{
public:
    //////////////////////////////////////////////////////////////////
    //	Construction
	LinearTypes		();
	~LinearTypes	()	{};
};

Not a lot to it, is there? The key is in the LinearTypes constructor implementation …

//////////////////////////////////////////////////////////////////////
//	class LinearTypes
//////////////////////////////////////////////////////////////////////
LinearTypes::LinearTypes		()
{
	types_.push_back (LINE_ELM);
	types_.push_back (LINE_STRING_ELM);
	types_.push_back (ARC_ELM);
	types_.push_back (CURVE_ELM);
	types_.push_back (CMPLX_STRING_ELM);
	types_.push_back (BSPLINE_CURVE_ELM);
	types_.push_back (MULTILINE_ELM);
}

Whenever we instantiate a LinearTypes object, its internal list of element type(s) is created automatically. Now it's very simple to set up your locate mask or scan criteria …

#include <SearchTypes.h>
LinearTypes linearTypes;   //  Internal type list is initialised in the constructor
  …
// Locate setup
linearTypes.SetLocateSearchMask ();
  …
// Scan Criteria setup
ScanCriteria*	pCriteria	= mdlScanCriteria_create ();
linearTypes.SetScanCriteria (pCriteria);

Download CSearchTypes Source Code

Download CSearchTypes Source Code

Download the CSearchTypes Source Code. You need Visual Studio 2005 to be installed in order to compile and link this code.

Return to MDL articles index.