Here are answers to questions about MicroStation® structures MSElement, MSElementUnion,
and MSElementDescr that are posted from time to time on the
Bentley Discussion Groups.
The questions are posted by MDL developers.
Q
The MSElement and MSElementDescr type definitions are sometimes confusing
for a developer's first encounter with MDL programming.
Here are some typical questions …
MSElement variable? MSElementUnion variable? ElementRef variable? MSElementDescr variable? MSElementDescr* pointer variable? Complex Element from the MDL programmer's view? MSElementDescr* pointer variable? A
The MSElementUnion structure is a union of all possible element structures.
This C language mechanism lets multiple data types share a common area of memory.
You most often use an MSElementUnion when creating a new element, before adding to
the DGN model.
A pointer to a MSElementDescr structure is often used for manipulating existing elements.
You should never declare a variable of type MSElementDescr, but instead declare a
pointer variable to MSElementDescr*.
Look in #include file <mselems.h>. You will see that it defines a union of element types …
union msElementUnion
There are additionally two typedefs …
typedef union msElementUnion MSElementUnion; typedef union msElementUnion MSElement;
In other words, MSElement and MSElementUnion are exactly the same data type.
When you create a variable of type MSElement on the stack, the compiler allocates memory …
MSElement element; // Compiler allocates sizeof (MSElementUnion) bytes, which is ~128KByte
The MSElementUnion structure is large enough to let you create any type of element.
For example, you can create a Line String element having 2,500 vertices in a single MSElement variable.
Most of the time, an MSElementUnion variable is rather overweight compared to the element
you want to create. A Line element, for example, has only two vertices, and most of the
memory in an MSElementUnion variable is unused.
When you want to create a new element, you have no alternative to using an MSElementUnion.
However, when you want to read an existing element, an MSElementUnion variable is usually
a waste of memory: instead, use an MSElementDescr* pointer variable.
The ElementRef opaque pointer references elements stored in a DGN model.
It's a fast way to get at an element and is explained in MDL help section ElementRef Functions.
The MSElementDescr structure is defined in <mselems.h>.
MSElementDescr is designed to be used as a pointer variable. In other words,
you never declare a variable of type MSElementDescr …
MSElementDescr descriptor; // Never do this!
Rather, you should always declare a pointer variable that you can pass to the mdlElmdscr_xxx functions …
MSElementDescr* pElement = NULL; // Always initialise pointer variables
In many cases, you read an element from file into an element descriptor. The MDL function allocates memory for the descriptor, and it is your duty to free the memory when you have finished using the descriptor …
MSElementDescr* pElement = NULL;
if (0 < mdlElmdscr_read (&pElement, ..., ... ))
{
// Do something with descriptor
...
// Free memory allocated by MicroStation
mdlElmdscr_freeAll (&pElement);
}
In MicroStation terminology, a complex element is a container of simpler elements.
Those simpler elements may each be a primitive MSElement.
The first element of a complex element is known as the header element, and is non-graphical.
Examples of complex element are cells, text nodes, complex string, and complex shapes.
You deal with a complex element in MDL by reading an element descriptor.
MicroStation performs the house-keeping of reading each element from file and creating a descriptor chain.
The descriptor chain is dynamically allocated and is a linked list of MSElementDescr* pointers.
Examine mselems.h to see the structure of MSElementDescr, and note the header fields.
In particular, MSElementDescr.h.firstElem and MSElementDescr.h.next are invaluable when interpreting a complex element.
A complex element may contain other complex components, which are termed nested components. A nested complex component has its own header and is structured just like its parent. This symmetric structuring suggests that recursive programming is a good way to handle nested complex elements.
MSElementDescr* pointers lend themselves to iteration through pointer arithmetic.
Because a complex element's header contains a pointer to the next element in the complex chain,
it's easy to iterate …
#includeMSElementDescr* pComplex = NULL; // Read a complex element from somewhereif (0 < mdlElmdscr_read (&pComplex, filePos, modelRef, FALSE, NULL)) { int elemType = 0;// Point to first graphic elementMSElementDescr* pComponent = pComplex->h.firstElem; while (pComponent) {// Analyse current componentelemType = mdlElement_getType (&pComponent->el); ...// Step to next elementpComponent = pComponent->h.next; }// Deallocate memory allocated by MicroStationmdlElmdscr_freeAll (&pComplex); }
MDL provides a function mdlElmdscr_operation that will iterate complex elements, including nested complex elements.
mdlElmdscr_operation may be overly complicated for simple operations, because …
ELMD_XXX operation flags require careful consideration
As an MDL developer, you must be aware of the large number of #include files provided
for your benefit. The #include files are header files to be included in your
MDL source code (.mc) and MDL resource data (.r) files. They are installed to the \mdl\include\ folder of your
MicroStation installation.
The #include file that provides the MSElement and MSElementDescr
structure and typedef definitions is <mselems.h>.
The #include files that provide the function prototypes for element
manipulation is <mselemen.fdf>, and for element descriptors is <mselmdsc.fdf>.
Your MDL code (.mc) file should include those files somewhere near the beginning …
#include <mselems.h>// MSElement and MSElementDescr definitions#include <mselemen.fdf>// mdlElement_xxx function prototypes#include <mselmdsc.fdf>// mdlElmdscr_xxx function prototypes
Return to MDL articles index.