Adding commands to MDL applications


You may have noticed that there are many typing commands in MicroStation. The Key-in icon can be found in the Primary group of the Home card. The Key-in Command dialog box can be opened, where you can browse and execute all currently available key-in commands, as shown in the figure below. Although only four columns are listed in this dialog box, each typing command can consist of more than 4 words.

These typing commands have many uses: for example,  

Typing commands is an important link between the MicroStation graphical user interface and background code execution. The four uses of the typed commands listed above are shown below: 

MDLCmd1

[1][2]

[3]

MDLCmd2

[4]

Add Command Functions:

Below we step by step to add command functions to the existing HelloWorld project. 

1. Create a new text file HelloWorldCmd.r in Visual Studio, copy the following content into this file, and save it.

#include <Mstn\MdlApi\rscdefs.r.h> 
#include <Mstn\MdlApi\cmdclass.r.h> 
/*----------------------------------------------------------------------+ 
|   Local Defines 
+----------------------------------------------------------------------*/ 

enum CmdTableIds 
{ 
   CT_NONE = 0, 

   CT_MAIN, 

   CT_SUB, 

   CT_CREATE 
}; 

/*----------------------------------------------------------------------+ 
|   HelloWorld commands 
+----------------------------------------------------------------------*/ 

CommandTable   CT_MAIN = 
{  
    { 1,  CT_SUB,PLACEMENT,  REQ,"HELLOWORLD" },  
}; 

CommandTable   CT_SUB = 
{ 
    { 1,  CT_CREATE, INHERIT, NONE, "CREATE" }, 
}; 

CommandTable   CT_CREATE = 
{ 
    { 1,  CT_NONE, INHERIT,   DEF, "Line" }, 

    { 2,  CT_NONE, INHERIT,   NONE, "ComplexShape" }, 

    { 3,  CT_NONE, INHERIT,   NONE, "ProjectedSolid" }, 

    { 4,  CT_NONE, INHERIT,   NONE, "BsplineSurface",}, 
};
 

The above command table file defines the following 6 key-in commands, each of which has a corresponding command number. Now create a HelloWorldCmd.h file and add the following:

#define CMD_HELLOWORLD                                     0x0100000000000000UI64  
#define CMD_HELLOWORLD_CREATE                              0x0101000000000000UI64   
#define CMD_HELLOWORLD_CREATE_LINE                         0x0101010000000000UI64   
#define CMD_HELLOWORLD_CREATE_COMPLEXSHAPE                 0x0101020000000000UI64   
#define CMD_HELLOWORLD_CREATE_PROJECTEDSOLID               0x0101030000000000UI64   
#define CMD_HELLOWORLD_CREATE_BSPLINESURFACE               0x0101040000000000UI64   

In the above HelloWorldCmd.r command table file: 

2. Open the HelloWorld.mke file in Visual Studio and make the following two changes:

a. Change appRscs = $(o)$(appName).rsc to  appRscs=$(o)$(appName).rsc $(o)$(appName)Cmd.rsc 

b. Add the lines :

$ (baseDir)$(appName) Cmd.h : $(baseDir)$(appName)Cmd.r

$(o)$(appName)Cmd.rsc: $(baseDir)$(appName)Cmd.r 

Note: The above code contains two very important blank lines. You cannot delete them at will. A blank line indicates that the target file is generated according to the default rules. During the compilation process, bmake will find the .r .h and .r .rsc rules in the mdl.mki file to compile and generate.

#--------------------------------------------------------
#   MicroStation CONNECT  HelloWorld.mke
#	Fixed by LA Solutions
#--------------------------------------------------------
PolicyFile = MicroStationPolicy.mki
DEFAULT_TARGET_PROCESSOR_ARCHITECTURE=x64

appName=HelloWorld
appObjs = $(o)$(appName)$(oext)
appRscs = $(o)$(appName).rsc $(o)$(appName)Cmd.rsc 
baseDir = $(_MakeFilePath)
mdlLibs = $(MSMDE)library/

%include mdl.mki

#--------------------------------------------------------
# Create needed output directories if they don't exist
#--------------------------------------------------------

always:
	 ~mkdir $(o)
	 ~mkdir $(rscObjects)
	 ~mkdir $(reqdObjs)
	 
#--------------------------------------------
#	Create command table and header file
#--------------------------------------------
$(baseDir)$(appName)Cmd.h:$(baseDir)$(appName)Cmd.r 

$(o)$(appName)Cmd.rsc: $(baseDir)$(appName)Cmd.r

#--------------------------------------------------------
# Define macros for files included in our link and resource merge
#--------------------------------------------------------
DLM_NO_SIGN       = 1
DLM_OBJECT_DEST   = $(o)
DLM_NAME    =$(appName)
DLM_NO_DLS   = 1
DLM_NO_DEF   = 1
DLM_NOENTRY   = 1
DLM_OBJECT_FILES        = $(appObjs)
DLM_NO_MANIFEST   = 1
DLM_DEST   = $(mdlapps)

LINKER_LIBRARIES        = $(mdlLibs)bentley.lib \
                          $(mdlLibs)BentleyAllocator.lib \
                          $(mdlLibs)mdlbltin.lib \
                          $(mdlLibs)RmgrTools.lib \
                          $(mdlLibs)BentleyGeom.lib \
                          $(mdlLibs)DgnPlatform.lib \
                          $(mdlLibs)dgnview.lib
						 
						 

						 
						 

#-----------------------------------------------------------------------
#   Generate resource files
#-----------------------------------------------------------------------
$(o)$(appName).rsc: $(baseDir)$(appName).r




#---------------------------------------------
#	Merge the app resources using rlib
#---------------------------------------------
$(o)$(appName).mi    : $(appRscs)
	$(msg)
	> $(o)make.opt
	-o$@
	$(appRscs)
	<
	$(RLibCmd) @$(o)make.opt
	~time

appRscs =   \
    $(o)$(appName).mi \
    $(o)$(appName).rsc

$(DLM_DEST)$(appName).ma         : $(appRscs)
        $(msg)
        > $(rscObjects)make.opt
        -o$@
        $(appRscs)
        <
        $(RLibCmd) @$(rscObjects)make.opt
        ~time

#-----------------------------------------------------------------------
#	Builds any necessary C++ CODE modules and link them to DLL
#-----------------------------------------------------------------------
$(o)$(appName)$(oext):$(baseDir)$(appName).cpp
%include dlmlink.mki

# Blank line above required for MKE/MKI files, this line for editors that rip.

3. Here are a few changes to HelloWorld.cpp: 

a. Include HelloWorldCmd.h.

#include <Mstn\MdlApi\MdlApi.h> 
#include <DgnPlatform\DgnPlatformApi.h> 
#include "HelloWorldCmd.h" 

b. Delete some original function calls in the MdlMain function, add command numbers and dynamic array definitions of command processing functions, open resource files, load command tables, and register command numbers. 

MdlCommandNumber cmdNums[] = 
{ 
{ (CmdHandler)createALine,        CMD_HELLOWORLD_CREATE_LINE }, 
{ (CmdHandler)createAComplexShape,    CMD_HELLOWORLD_CREATE_COMPLEXSHAPE }, 
{ (CmdHandler)createAProjectedSolid,CMD_HELLOWORLD_CREATE_PROJECTEDSOLID }, 
{ (CmdHandler)createABsplineSurface,    CMD_HELLOWORLD_CREATE_BSPLINESURFACE }, 
0 
}; 

extern "C" DLLEXPORT void MdlMain(int argc, WCharCP argv[]) 

{ 
ModelInfoCP pInfo = ACTIVEMODEL->GetModelInfoCP(); 
g_1mu = pInfo->GetUorPerStorage(); 

RscFileHandle rscFileH; 
mdlResource_openFile(&rscFileH, NULL, RSC_READONLY); 
mdlParse_loadCommandTable(NULL); 

mdlSystem_registerCommandNumbers(cmdNums); 
} 

The mdlResource_openFile in the above code opens the resource file HelloWorld.ma, and then mdlParse_loadCommandTable loads the command table resource from it. The correspondence between the command processing function and each command number is defined in the dynamic array cmdNums, and then registered by mdlSystem_registerCommandNumbers. We did not define a set of command names at the same time as in most MDL examples (requires registration with mdlSystem_registerCommandNames), because the use of command names is far less convenient than typing commands. The use of command names is only necessary if you have not defined a command table. 

c. Make some modifications to createALine, createAComplexShape, createAProjectedSolid, and createABsplineSurface to meet the requirements as command processing functions. At the same time, the assignment of basePt is built into each function. The modified code is as follows: 

/*-------------------------------------------------------------+ 
|   HelloWorld.cpp                                                | 
+-------------------------------------------------------------*/ 

#include <Mstn\MdlApi\MdlApi.h> 
#include <DgnPlatform\DgnPlatformApi.h> 
#include "HelloWorldCmd.h"
 

USING_NAMESPACE_BENTLEY_DGNPLATFORM 
USING_NAMESPACE_BENTLEY_MSTNPLATFORM 
USING_NAMESPACE_BENTLEY_MSTNPLATFORM_ELEMENT 

 
double g_1mu; 

 
void createALine(WCharCP unparsed) 
{ 
DPoint3d basePt = DPoint3d::FromZero(); 
EditElementHandle eeh; 
DSegment3d seg; 

seg.Init(basePt, DPoint3d::From(basePt.x + g_1mu * 2, basePt.y + g_1mu)); 

ICurvePrimitivePtr pCurve = ICurvePrimitive::CreateLine(seg); 

DraftingElementSchema::ToElement(eeh, *pCurve, nullptr, ACTIVEMODEL->Is3d(), *ACTIVEMODEL); 

eeh.AddToModel(); 

} 

 

void createAComplexShape(WCharCP unparsed) 
{ 

EditElementHandle eeh; 

    MSElement el; 
    MSElementDescrP  edP = NULL; 
   DPoint3d         basePt, pts[3]; 

 
   basePt.x = 1.7*g_1mu;   basePt.y = -0.3*g_1mu;    basePt.z = -0.6*g_1mu; 
   mdlComplexChain_createHeader (&el, 1, 0); 
   mdlElmdscr_new (&edP, NULL, &el); 
   pts[0] = pts[1] = pts[2] = basePt; 

pts[1].x += g_1mu*0.3;    pts[1].y += g_1mu*0.7; 

pts[0].x += g_1mu;        pts[0].y += g_1mu; 

DEllipse3d arcPts = DEllipse3d::FromPointsOnArc(pts[2], pts[1], pts[0]); 

CurveVectorPtr pCurveVec = CurveVector::Create(CurveVector::BOUNDARY_TYPE_Outer); 

pCurveVec->Add(ICurvePrimitive::CreateArc(arcPts)); 

 

pts[1].x = pts[0].x;    pts[1].y = pts[2].y; 

pCurveVec->Add(ICurvePrimitive::CreateLineString(pts, 3)); 

DraftingElementSchema::ToElement(eeh, *pCurveVec, nullptr, ACTIVEMODEL->Is3d(), *ACTIVEMODEL); 

eeh.AddToModel(); 

} 

 

void createAProjectedSolid(WCharCP unparsed) 
{ 
   DPoint3d         basePt, pts[6]; 

   basePt.x = 3.2*g_1mu;     basePt.y = -0.6*g_1mu;    basePt.z = -1.2*g_1mu; 
   pts[0] = basePt; 

pts[0] = basePt; 
pts[1].x = pts[0].x;               pts[1].y = pts[0].y - g_1mu / 2;   pts[1].z = pts[0].z; 
pts[2].x = pts[1].x + g_1mu / 2;   pts[2].y = pts[1].y;               pts[2].z = pts[0].z; 
pts[3].x = pts[2].x;               pts[3].y = pts[2].y - g_1mu / 2;   pts[3].z = pts[0].z; 
pts[4].x = pts[3].x + g_1mu / 2;   pts[4].y = pts[3].y;               pts[4].z = pts[0].z; 
pts[5].x = pts[4].x;               pts[5].y = pts[0].y;               pts[5].z = pts[0].z; 

 

CurveVectorPtr pCurveVec = CurveVector::CreateLinear(pts, 6, CurveVector::BOUNDARY_TYPE_Outer); 

DVec3d extrusionVec = DVec3d::From(0, 0, g_1mu); 

DgnExtrusionDetail  data(pCurveVec, extrusionVec, true); 

ISolidPrimitivePtr pSolid = ISolidPrimitive::CreateDgnExtrusion(data); 

EditElementHandle eeh; 

DraftingElementSchema::ToElement(eeh, *pSolid, nullptr, *ACTIVEMODEL); 

eeh.AddToModel(); 

} 

 

void createABsplineSurface(WCharCP unparsed) 

{ 
   
   MSBsplineSurface  bsSurface; 
   MSBsplineCurve    bsCurves[4]; 
   DPoint3d                basePt, arcPts[4][3]; 

   
   basePt.x = 5.4*g_1mu;     basePt.y = -2.3*g_1mu;    basePt.z = -1.8*g_1mu; 
   arcPts[0][0] = arcPts[0][1] = arcPts[0][2] = basePt; 



DPoint3d         center[4]; 

RotMatrix        rMatrix[4]; 

double           radius = g_1mu / 2; 

 

center[0] = center[1] = center[2] = center[3] = basePt; 

center[0].x += radius; 

center[1].x += g_1mu;     center[1].y += radius; 

center[2].x += radius;    center[2].y += g_1mu; 

center[3].y += radius; 

 

DVec3d xVec = DVec3d::From(1, 0, 0), negativeXVec = DVec3d::From(-1, 0, 0); 

DVec3d yVec = DVec3d::From(0, 1, 0), negativeYVec = DVec3d::From(0, -1, 0); 

DVec3d zVec = DVec3d::From(0, 0, 1); 

rMatrix[0].InitFrom2Vectors(xVec, zVec);  //Front View 

rMatrix[1].InitFrom2Vectors(yVec, zVec);  //Right View 

rMatrix[2].InitFrom2Vectors(negativeXVec, zVec);  //Back View 

rMatrix[3].InitFrom2Vectors(negativeYVec, zVec);  //Left View 

 

EditElementHandle eeh; 

for (int i = 0; i<4; i++) 

{ 

bsCurves[i].InitEllipticArc(center[i], radius, radius, 0, PI, &rMatrix[i]); 

} 

if (SUCCESS == mdlBspline_coonsPatch(&bsSurface, bsCurves)) 

{ 

DraftingElementSchema::ToElement(eeh, bsSurface, nullptr, *ACTIVEMODEL); 

eeh.AddToModel(); 

mdlBspline_freeSurface(&bsSurface); 

} 

for (int i = 0; i<4; i++) 

mdlBspline_freeCurve(&bsCurves[i]); 

} 

 

MdlCommandNumber cmdNums[] = 
{ 
{ (CmdHandler)createALine,        CMD_HELLOWORLD_CREATE_LINE }, 
{ (CmdHandler)createAComplexShape,    CMD_HELLOWORLD_CREATE_COMPLEXSHAPE }, 
{ (CmdHandler)createAProjectedSolid,CMD_HELLOWORLD_CREATE_PROJECTEDSOLID }, 
{ (CmdHandler)createABsplineSurface,    CMD_HELLOWORLD_CREATE_BSPLINESURFACE }, 
0 
}; 

extern "C" DLLEXPORT void MdlMain(int argc, WCharCP argv[]) 
{ 
ModelInfoCP pInfo = ACTIVEMODEL->GetModelInfoCP(); 
g_1mu = pInfo->GetUorPerStorage(); 
RscFileHandle rscFileH; 
mdlResource_openFile(&rscFileH, NULL, RSC_READONLY); 
mdlParse_loadCommandTable(NULL); 
mdlSystem_registerCommandNumbers(cmdNums); 
}

4. Return to the command prompt of MicroStation CONNECT Edition SDK and type bmake to generate the project. In the process of generating the project, if any yellow or red prompt message appears, it indicates that there are warnings or errors, which need to be eliminated before the executable ma and dll can be finally generated. 

Note: Before executing bmake, you must type mdl unload helloworld into the MicroStation software to unload the loaded application, otherwise an error message will appear during bmake that cannot overwrite the original DLL.

5. Go back to MicroStation and open the Key-in dialog. Type MDL LOAD HelloWorld to load the application. After entering HelloWorld again, you will see the command typing tree shown in the following figure, which corresponds exactly to what we defined in HelloWorldCmd.r. Enter one of these commands and press Enter to draw the corresponding part of the graph. 

Prev:[[Creating elements in MDL applications]]Next:[[Creating a VS project and debugging the application]]