0% found this document useful (0 votes)
251 views12 pages

Getting Started With MDL in MicroStation V8

The document discusses changes needed to recompile MDL applications for MicroStation V8. Key changes include updating make files to use new macros like "mLinkCmd" instead of "LinkCmd", adding new libraries to the linker, and updating code to handle new features in V8 like unlimited levels, models, and Unicode support. The core application code will need changes to work as before and take advantage of new capabilities, such as using the new ScanCriteria API instead of the old scanning functions. Models replace file numbers, and elements now need to check that levels exist before using them.

Uploaded by

Navneet kumar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
251 views12 pages

Getting Started With MDL in MicroStation V8

The document discusses changes needed to recompile MDL applications for MicroStation V8. Key changes include updating make files to use new macros like "mLinkCmd" instead of "LinkCmd", adding new libraries to the linker, and updating code to handle new features in V8 like unlimited levels, models, and Unicode support. The core application code will need changes to work as before and take advantage of new capabilities, such as using the new ScanCriteria API instead of the old scanning functions. Models replace file numbers, and elements now need to check that levels exist before using them.

Uploaded by

Navneet kumar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

Getting started with MDL in MicroStation V8

With all the benefits that MicroStation V8 provides, MDL applications need to be recompiled.
This is mainly due to many changes in underlying structures. Now is as good a time as any to
revisit the roots of MDL.

The documentation now covers more functions than ever before. New features have been
added and support for "Native Code" has been enhanced -- in fact, some of the core concepts
have changed quite significantly. For applications to take advantage of these enhancements,
some change is required. But the good news is that in the majority of cases, the logic of the
application should remain the same.

Where should you start? With all MDL applications, the Make file must be addressed first.
The Make file syntax has remained constant since its introduction; the new changes apply
only to some of the macros and they have been in place since MicroStation/J. The macro
"LinkCmd" is now "mLinkCmd" and "rscLibCmd" is now "rLibCmd." These changes will be
evident during compilation when the compile process stops and a system error message
appears. Another change to the Make files is the move to "Native Code," which creates .dlo
file references that take the place of .ml files.

One of the often used, but rarely understood, parameters found in Make files is -s, which sets
the stack size. The -s parameter is only necessary when you are not using the common stack,
which few applications need.

The next thing that you will notice about the compile process is the new options that make the
compiler stricter. By default, the compiler is set to the highest level of checking. Although not
recommend, you can decrease the level of "compiler strictness" by using the dNOSTRICT
option. This may be useful for getting started but should definitely not be used for final
compilation. Also helpful is the -X option, which will force short path names to be used. This
may be necessary in some instances since, by default, MicroStation is installed to the Program
Files directory structure.

Another change in the environment that goes with this move to "Native Code," is that
additional libraries have been added to the MLINK_STDLIB environment variable. This
variable should now include toolsubs.dlo and dgnfileio.dlo. By starting in the Make file, the
application build process can be utilized to find any changes in the source code that have to be
made. While getting the build to go through, it may be necessary to comment out or #ifdefine
sections of the code to allow the application to build.

The GUI for applications should only need changing for applications that call upon the level
map or level picker items. These items are no longer valid with unlimited level support in V8
DGN files. Other than those two instances, dialogs should function as they always have, with
one exception: some of the spacing may need to be updated, because the dialog font
information has changed.

The big news is the addition of tree items and editable List box cells. There is a replacement
for the StringList in List boxes, which may be useful for applications. The new ListModel
should make the alignment of data and its presentation easier and more powerful than ever
before. The ListModel allows you to separate the data from the presentation in the List box.
pListCell = mdlListRow_getCellAtIndex (pRow, CELLNAMECOL);
mdlListCell_setStringValueW (pListCell,wCellName,TRUE);
iStatus = mdlListCell_setInfoField
(pListCell,0,(long)pChildNode);
long infoField;
iStatus = mdlListCell_getInfoField (pListCell,0,&infoField);
//do the description column
mdlModelItem_getDescription (pDgnIndexItem, wCellDescription,
MAX_CELLDSCR_LENGTH);
pListCell = mdlListRow_getCellAtIndex (pRow,CELLDESCCOL);
mdlListCell_setStringValueW (pListCell,wCellDescription,TRUE);

Conversion from StringList to ListModel is not a necessary change, but it is certainly a simple
change that will result in a better application in the long run. To make the list cell editable, the
application needs to add an attribute to the List box definition. In the application, the code
will then need to provide a hook for the edit event or use a simple text field as the editor.
/*----------------------------------------------------------------------
+*//**
* creates the listmodel that the listbox will use.
*
* @bsimethod createListBox *
*
* @param nCols number of columns in the listbox *
* @return the listmodel to connect to the listbox
*
* *
+----------------------------------------------------------------------*/
Private ListModel* createListBox
(
int nCols
)
{
ListModel *pListModel;
ListRow *pRow;
int i;
int colIndex;

pListModel = mdlListModel_create (nCols);

for (i=0;i<6 ;i++)


{
pRow = mdlListRow_create (pListModel);
for (colIndex=0;colIndex<nCols ;colIndex++ )
{
ListCell *pCell;
pCell = mdlListRow_getCellAtIndex (pRow,colIndex);
mdlListCell_setStringValue (pCell,"X",TRUE);
if (colIndex==1)
mdlListCell_setEditor
(pCell,RTYPE_ComboBox,COMBOBOXID_DataSheetEditor,
mdlSystem_getCurrMdlDesc(),FALSE,TRUE);
if (colIndex == 0)
mdlListCell_setEditor
(pCell,RTYPE_Text,TEXTID_UserInfo,mdlSystem_getCurrMdlDesc (), FALSE,TRUE);

if (colIndex == 2)
mdlListCell_setEditor (pCell, RTYPE_Container,
CONTAINERID_TextStyleColorPicker, mdlSystem_getCurrMdlDesc(), FALSE, TRUE);
}
mdlListModel_addRow (pListModel,pRow);
}

return pListModel;
}

The place where applications will see the most change is in the core application code. This
can be divided into two sections: one for changes to make the application work just as it did in
previous versions, and the other to allow changes to take advantage of new capabilities in
MicroStation V8.

The first thing anyone will notice about MicroStation V8 is the new level structure, which
gives you "unlimited levels." This could affect applications in a number of ways, since
historically the level information was held in an array of short integers. Level information is
now a BitMask data type. Another thing to note regarding levels is that each element holds on
to an unsigned long integer, which is the internal level ID. Since not every level exists in
every file any more (due to the potential use of DGN Libraries), any code that creates an
element that should be on a particular level will need to first ensure that level exists;
otherwise the element will end up on the default level.

MicroStation V8 also introduces the concept of Models. This is reflected in applications by


the introduction of the DgnModelRefP. This data type is used in place of the file number and
will require the programmer to find all the integral references for files to be updated. Along
with this update, those places with comparisons based on a file number will need to be
updated to use the API for querying the model reference.

Another enhanced capability in MicroStation V8 is scanning. While the API for scanning a
DGN file is still in place and will still work for most applications, the new ScanCriteria API
should be used instead. ScanCriteria offers new capabilities like callbacks and an API for
setting the criteria. This API is no longer shared, so things like performing scans within a scan
are no longer issues.

Most information in the DGN file that involves storing character data is now stored in
Unicode. To convert between Multibyte and Unicode character format, you can use the utility
functions mdlCnv_convertMultibyteToUnicode and mdlCnv_convertUnicodeToMultibyte.
Strings in Unicode format will be needed for cell names, level names, and model names.

Models are essentially the encapsulation of DGN files within a DGN file. DGN files can have
more than one model in the file. There are some parts that are shared between the models, like
shared cell definitions and level tables. When an application needs to copy elements from one
model to another the mdlElmdscr_copy function should be used. This copy function uses the
mdlCopyContext to remap the association information from one file to another.

To process a set of Models the application will need to create a ModelRefIterator then set it to
go through the set of Models. The options are to process the current model and its references
and then to control the depth to which the iteration will process. An example for processing
the current active modelreference and all of its references including the nested references:
int iteratorType =
MRITERATE_Root|MRITERATE_PrimaryChildRefs|MRITERATE_ExtendedChildRefs;
ModelRefIteratorP iterator = NULL;
DgnModelRefP pModelRef = mdlModelRef_getActive ();

status = mdlModelRefIterator_create (&iterator, pModelRef,


iteratorType, -1);

while (NULL != (currentModelRef = mdlModelRefIterator_getNext


(iterator)))
{
//do some process here!!!
}
mdlModelRefIterator_free (&iterator);

References are also ModelReferences. The process used to attach a reference has been
improved by making it a three-step process. First, begin the attachment by using the
mdlRefFile_beginAttachment, then set the parameters of the attachment, and finally complete
the attachment by using mdlRefFile_completeAttachment.
/*----------------------------------------------------------------------+
| |
| name createAttachments |
| |
+----------------------------------------------------------------------*/
cmdName void createAttachments
(
char *unparsed
) cmdNumber CMD_REFATT_SIMPLE
{
char fileName[MAXFILELENGTH];
int status;
DgnModelRefP modelRefP;
FileOpenParams fParams;
BoolInt isThreeD;
int noSnap = 0;
DgnFileObjP dgnFileObjP;
int format;
int lastAction;

if (!*unparsed)
{
memset (&fParams,0,sizeof fParams);
mdlDialog_fileOpenExt (fileName,NULL,&fParams,0);
}
else
if (SUCCESS != mdlFile_find (fileName, unparsed, NULL, NULL))
{
char buffer[80];
snprintf ("File %s not found\n",sizeof buffer, unparsed);
mdlDialog_openAlert (buffer);
return;
}

status = mdlFile_checkDesignFile (&isThreeD, fileName);


status = mdlWorkDgn_openFile (&modelRefP,
&format,&isThreeD,fileName,NULL,TRUE);
dgnFileObjP = mdlModelRef_getDgnFile (modelRefP);
gListModelData.dgnFileObjP = dgnFileObjP;
mdlDialog_openModal (&lastAction,NULL,DIALOGID_ListModels);
if ((SUCCESS == status ) &&(ACTIONBUTTON_OK != lastAction))
{
return;
mdlWorkDgn_closeFile (modelRefP);
}
if(SUCCESS != (status = mdlRefFile_beginAttachment
(&modelRefP,fileName,gListModelData.modelName,
gListModelData.modelDescription, L"Attached via code")))
{
printf ("Error creating attachment %d\n", status);
return;
}
else
{
mdlRefFile_setParameters ( (void *)noSnap, REFERENCE_SNAP,
modelRefP);

if(SUCCESS != (status = mdlRefFile_completeAttachment


(modelRefP,REFATTACH_NEST_DISPLAY ,-1,TRUE)))
{
printf ("Error completing attachment %d\n", status);
return;
}
}
else// the model is not in the file
{
mdlOutput_messageCenter (MESSAGE_ERROR,"Model Not Found","The model
is not in the file that you have selected to attach",TRUE);
}
}

Another capability of models is that they can be cells. In MicroStation V8, the cell now
contains scale information to allow for "true scale" placement in designs. Cell libraries are
now DGN files that contain a collection of models. 3D cells also can be placed in 2D DGN
files -- the cell will simply be "flattened." By using the mdlCell_getElmDscrExtended, the
application can control the orientation of the "flattening" as well as the level information that
is copied into the destination file.

There are two main parts to the level system that application developers will need to work
with. The first is the level itself, and the other is the level table that contains the available
levels in a file. To determine the levels that are in a file, the application will need to create a
level iterator that will loop through all the levels.

///iterate through the level information


// mdlLevelTable_traverse
(MASTERFILE,traverseFunction,(void*)&tInfo,FILTER_NULL_ID);
pLevelIterator = mdlLevelIterator_create (MASTERFILE);
mdlLevelIterator_traverse
(pLevelIterator,traverseFunction,(void*)&tInfo);
mdlLevelIterator_free (&pLevelIterator);

/*----------------------------------------------------------------------
+*//**
* Callback function for processing the members of the level table in a
file.
*
* @bsimethod traverseFunction *
*
* @param levelID The ID number of the level to be processed
*
* @param dataP This is the data added to the function by the
implementation. *
* @return SUCCESS to continue or !SUCCESS to stop processing
*
* *
+----------------------------------------------------------------------*/
DLLEXPORT Public int traverseFunction
(
ULong levelID,
void *dataP
)
{
TraverseInfo *tInfoP=dataP;
MSWChar levelNameOutput[MAX_LEVEL_NAME_LENGTH*2];
ULong color, style, weight;
ListRow *pRow;
BoolInt override;
LineStyleParams lsStyleInfo;

pRow = mdlListRow_create (tInfoP->pListModel);


tInfoP->pMdlDesc=mdlSystem_findMdlDesc ("lvlmangr");
if (!tInfoP->pMdlDesc)
{
mdlSystem_loadMdlProgram ("lvlmangr.ma","lvlmangr",NULL);
tInfoP->pMdlDesc=mdlSystem_findMdlDesc ("lvlmangr");
}
override=0;
/* Get level name */
if (SUCCESS == mdlLevel_getName
(levelNameOutput,MAX_LEVEL_NAME_LENGTH*2, MASTERFILE,levelID))
{
ListCell *pCell;
char lvlName[255];
pCell = mdlListRow_getCellAtIndex (pRow, COL_NAME);
lvlmangr_setLevelNameAtColumn (pRow, COL_NAME,
levelNameOutput);
mdlCnv_convertUnicodeToMultibyte
(levelNameOutput,0,lvlName,255);
mdlListCell_setStringValueW (pCell, levelNameOutput, TRUE);
mdlListCell_setInfoField (pCell, 0, 0);
if (tInfoP->activeLevel == levelID)
mdlListCell_setFontIndex (pCell, FONT_INDEX_BOLD);

mdlListCell_setInfoField (pCell, 1, levelID);


}

/* Get lock status */


lvlmangr_setLevelLockAtColumn (pRow, COL_LOCK,
mdlLevel_isElementLocked (MASTERFILE,levelID), tInfoP->pMdlDesc);

/* Get fileNumber */
lvlmangr_setLevelFilenumberAtColumn (pRow, COL_REFERENCE,
MASTERFILE);

/* Get level color */


if (SUCCESS == mdlLevel_getElementColor (&color,
MASTERFILE,levelID))
{
lvlmangr_setLevelColorAtColumn (pRow, COL_COLOR, color,
override, tInfoP->pDB,tInfoP->pMdlDesc);
}

/* Get level style */


if (SUCCESS == mdlLevel_getElementStyle
(&style,&lsStyleInfo,MASTERFILE,levelID))
{
lvlmangr_setLevelStyleAtColumn (pRow, COL_STYLE, style,
override,tInfoP->pMdlDesc);
}

/* Get level weight mdlLevel_getWeight()*/


if (SUCCESS == mdlLevel_getElementWeight (&weight,
MASTERFILE,levelID))
{
lvlmangr_setLevelWeightAtColumn (pRow, COL_WEIGHT, weight,
override,tInfoP->pMdlDesc);
}

/* Get plot status */


lvlmangr_setLevelPlotAtColumn (pRow, COL_PLOT, FALSE,tInfoP-
>pMdlDesc);

/* Get level description */


if (SUCCESS == mdlLevel_getDescription
(levelNameOutput,MAX_LEVEL_NAME_LENGTH*2, MASTERFILE,levelID))
{
lvlmangr_setLevelDescriptionAtColumn (pRow, COL_DESCRIPTION,
levelNameOutput);
}

/* Get used status */


lvlmangr_setLevelUsedAtColumn (pRow, COL_USED, FALSE,tInfoP-
>pMdlDesc);

mdlListModel_insertRow (tInfoP->pListModel, pRow, -1);

return SUCCESS;
}

The level system can be used to create filters to reduce the number of levels that are actively
displayed. One of the data types used in working with levels is a BitMask. Access to the
BitMask is gained through the API.
/*----------------------------------------------------------------------
+*//**
* This function sets the levels to use in the scan from a string that is
passed into it.
*
* @bsimethod kscanSetLevelsFromString
* *
* @param *pScanCriteria The ScanCriteria to establish *
* @param *levelString The string that represents the levels to
scan on. *
* @param modelRef The model that is used to set the level
information. *
* @return SUCCESS to continue not SUCCESS to stop the scan. *
* *
+----------------------------------------------------------------------*/
Private void kscanSetLevelsFromString
(
ScanCriteria *pScanCriteria,
char *levelString,
DgnModelRefP modelRef
)
{
BitMask *pBitMask;

mdlBitMask_create (&pBitMask,FALSE);
mdlBitMask_setFromString (pBitMask,levelString,0,64); //this uses level
ids!
mdlScanCriteria_setLevelTest (pScanCriteria,pBitMask,TRUE,TRUE);
return;
}

/*----------------------------------------------------------------------
+*//**
* This function sets the levels to scan based on those visible in the view
passed in.
*
* @bsimethod kscanSetScanLevelsFromView
* *
* @param *pScanCriteria The ScanCriteria to set *
* @param viewNum The view number to get the display information
from. *
* @param modelRefP The model that the level information is from.
*
* @return SUCCESS to continue not SUCCESS to stop the scan. *
* *
+----------------------------------------------------------------------*/
Private void kscanSetScanLevelsFromView
(
ScanCriteria *pScanCriteria,
int viewNum,
DgnModelRefP modelRefP
)
{
const BitMask *pLevelMask = NULL;
if (NULL != (pLevelMask = mdlView_getLevelDisplayMask (modelRefP,
viewNum,VIEW_LEVEL_DISPLAY_TYPE_EFFECTIVE)))
mdlScanCriteria_setLevelTest(pScanCriteria, (BitMask *) pLevelMask,
FALSE,FALSE);
}

The scanning process still uses the same basic methodology, however, the new API makes it
much easier and more robust than ever. The ScanCriteria API almost completely mimics the
Scan API. The ScanCriteria is not shared, the tests are built through API calls, and it features
the ability to use callbacks to process the found data. The non-shared aspect of the
ScanCriteria allows scans within scans, since the context no longer needs to be saved and
restored. To create a ScanCriteria, the application will call the mdlScanCriteria_create and
when the application is finished, it must free the memory used by calling the
mdlScanCriteria_free.
ScanCriteria *pScanCriteria = mdlScanCriteria_create ();

mdlScanCriteria_setReturnType
(pScanCriteria,MSSCANCRIT_ITERATE_ELMDSCR,FALSE,TRUE);
mdlScanCriteria_setElmDscrCallback
(pScanCriteria,interogateElements,(void*)pListModel);
mdlScanCriteria_setModel (pScanCriteria,MASTERFILE);

mdlScanCriteria_scan (pScanCriteria,NULL,NULL,NULL);

mdlScanCriteria_free (pScanCriteria);

Many applications have an interactive element location as a part of the process. In


MicroStation/J, there were different methods for controlling the elements that were selected
into the application; some applications looked into the TCB for setting some global flags that
would refine the location process; some applications used callbacks on the before or after pick
event; and some used the MDL API. In MicroStation V8, the API is now the best way to
work. The TCB variables have either been removed or wrapped into the API. There are also
new parameters to the callbacks that allow you more access to information on the located
element.

For application changes, you will need to use the mdlLocate functions to set the element
mask. The new HitPath allows the application to query the related information on the element
that is selected. From the HitPath information, the application can look at other possible
elements that meet the location criteria and possibly adjust what is found. The callback
changes involve adding more parameters to the callback that include the HitPath.

/*----------------------------------------------------------------------+
| |
| name singlelocate_ElmFilter |
| |
+----------------------------------------------------------------------*/
Private int singlelocate_ElmFilter
(
int preLocate, /* => TRUE if pre-locate */
MSElementUnion *selElm, /* => current element */
DgnModelRefP modelRefP,
unsigned long filePosition,
Point3d *pointP,
int viewNumber,
HitPathP hitPath,
char *cantAcceptReason
)
{
ULong filePos;
DgnModelRefP currFile = MASTERFILE;
MSElementUnion headElm;
char reason[]="can only work for cells and text";

printf ("hit locate filter at %ld \n",preLocate);

strcpy (cantAcceptReason,reason);

/* during prelocate we passed the element that was originally


located,
this could be any part of a cell. so you do not have to locate
the
origin to accept the element the same comes true with text nodes
although the later problem is not as prevalent */
if (GLOBAL_LOCATE_IDENTIFY != preLocate && GLOBAL_LOCATE_AUTOLOCATE !=
preLocate)
return LOCATE_ELEMENT_NEUTRAL;
if (selElm->hdr.ehdr.complex == 1) /* is a complex element */
{
filePos = mdlElement_getFilePos (FILEPOS_CURRENT, &currFile);
#if defined (MSVERSION) && (MSVERSION >= 0x550)
mdlElement_read (&headElm, currFile, filePos);
#else
mdlElement_read ((UShort *)&headElm, currFile, filePos);
#endif
if ((headElm.hdr.ehdr.type == 7 && SCTinfo.sNode == -1) ||
(headElm.hdr.ehdr.type == 2 && SCTinfo.sCell == -1))
return LOCATE_ELEMENT_ACCEPT;
}
else if (selElm->hdr.ehdr.type == 17 && SCTinfo.sText == -1)
return LOCATE_ELEMENT_ACCEPT;
else
return LOCATE_ELEMENT_REJECT;
}

Within the locate process is AutoLocate. To enable that, the application must add the
mdlAutoLocate_enable function call. Other features in the locate process include the ability to
identify elements with a "flyover" balloon. To add this feature to the application, use the
mdlLocate_setFunction with the LOCATE_PROVIDE_PATH_DESCRIPTION. This
callback will look something like this:
/*----------------------------------------------------------------------
+*//**
* Demonstrates using the locate callback to modify the string shown when
* hovering over an element. *
* @param path The DisplayPath for this element
* @param description the description that is used in the balloon
these are the sum of all and limited to 256 chars
* @param path The reference path text for this element
* *
+----------------------------------------------------------------------*/
Public void myappLocateInfo
(
DisplayPathP path,
MSWChar* description,
MSWChar* refStr
)
{
ElementRef eRef;
MSElement el;
int eSize;

eRef = mdlDisplayPath_getCursorElem (path);


eSize = elementRef_getElement (eRef,&el,sizeof el);
if (ELLIPSE_ELM == mdlElement_getType (&el))
mdlWideChar_strcat (description, L" This is a Line");
else
mdlWideChar_strcat (description, L"Application X Was Here!");
}

Some may say that the most important capability in MicroStation V8 is the ability to develop
applications in "Native Code". That is a program written and built using Visual C rather than
the MDL tools. While the user interface is still MDL-based, the code portion is pure C/C++
and is compiled into a DLL. To make a "Native Code" application, first define a resource of
type DLLMDLAPP that will relate the application name to the DLL name.
#define DLLAPPID 1
/* associate app with dll */
DllMdlApp DLLAPPID =
{
"xmlSample", "xmlSample"
}

The source code is written in a .c or .cpp file to identify it as native. Within the source code is
an MdlMain that will take the place of the main in normal applications.
extern "C" DLLEXPORT int MdlMain
(
int argc,
char *argv[]
)
{
RscFileHandle rHandle;
void *setP= NULL;

mdlResource_openFile (&rHandle,NULL,RSC_READONLY);

mdlParse_loadCommandTable (NULL);
mdlSystem_registerCommandNames (cmdNames);
mdlSystem_registerCommandNumbers (cmdNumbers);

mdlDialog_hookPublish (sizeof uHooks/sizeof uHooks[0],uHooks);

return SUCCESS;
}

To integrate command numbers and command names, there are two structures that need to be
populated.
Private MdlCommandName cmdNames [] =
{
{xmlSample_mainDialog,"xmlSample_mainDialog" },
{placeLine_start,"placeLine_start"},
{xmlSample_addLargeXMLFragment,"buildLargeFragment"},
{xmlSample_buildDOMfromDB,"buildDOM"},
{xmlSample_purgeXMLData,"purgeDBXML"},
0
};
Private MdlCommandNumber cmdNumbers[] =
{
{placeLine_start,CMD_PLACE_XMLLINE},
{xmlSample_mainDialog,CMD_XMLSAMPLE_DIALOG},
{xmlSample_addLargeXMLFragment,CMD_DOMTEST_BUILD},
{xmlSample_buildDOMfromDB,CMD_DOMTEST_READ},

0
};

Within the MdlMain, the MdlCommandNumber and/or MdlCommandName structures are


published to the system using the appropriate mdlSystem function call. The memory
allocations will need to be done using the dlmSystem functions.

In the Make file, things will need to be rearranged to build the application with the correct
tools. To build a DLL, you need to use some additional rules that are defined in the dlmlink
and dlmcomp mki files. There are a number of macros that need to be defined for these files.
#------------------------------------------------
# Set up to use dlmcomp.mki and dlmlink.mki
#------------------------------------------------
dlmObjs = $(o)$(appName)$(oext)
DLM_NAME = $(appName)
DLM_SYM_NAME = $(appName)sym
DLM_RESL_NAME = $(appName)res
DLM_OBJECT_DEST = $(o)
DLM_LIBDEF_SRC = $(baseDir)
DLM_OBJECT_FILES = $(dlmObjs)
DLM_LIBRARY_FILES = $(dlmLibs)
DLM_NO_DLS = 1 # use DLLEXPORT instead
DLM_NO_DEF = 1
DLM_NOENTRY = 1
DLM_LIBRARY_FILES = $(mdlLibs)dgnfileio.lib \
$(mdlLibs)toolsubs.lib \
$(mdlLibs)ditemlib.lib \
$(mdlLibs)mdllib.lib \
$(mdlLibs)mgdshook.lib

#------------------------------------------------
# Compile the source files for the DLM
#------------------------------------------------
%include dlmcomp.mki

$(o)$(appName)$(oext): $(baseDir)$(appName).cpp

#------------------------------------------------
# Use dlmlink.mki to compile the Dynamic
# Load Specification (.dls) file and link
# the DLM.
#------------------------------------------------
%include dlmlink.mki

You will also need to have Microsoft's Visual C/C++ 6.0 SP4 development tools installed.
The caveat: the application will not run on Bentley PowerDraft since DLLs are not enabled.

Updating applications for MicroStation V8 should be accomplished in a few steps:

1. Get the compile operation to succeed


2. Get the application features to work as they did in previous versions
3. Take advantage of new capabilities in MicroStation.

The upgrade path should not involve major logic changes so it can become a mechanical
exercise to get the application to compile. Once the application is compiling, the process
becomes little more than refining the application to work with new and improved capabilities
in MicroStation.

You might also like