Publish Date: January 2006
Bentley recommends using "native code" for application development with MicroStation V8 XM Edition -- native code being a term for an application written in Visual C/C++ and compiled into a DLL. In this article, three points will be addressed; how to build a new native code application; what needs to be done to convert existing applications to native code; and finally how to work with native code applications (i.e. load, debug, etc.) Before getting started, you need to make sure you have the right development tools. For native code application development in MicroStation V8 XM Edition, you need VisualStudio .NET 2003 Edition, which includes VisualC/C++ 7.1. It is also recommended to use C++ rules for compiling, since those provide a stricter environment to build applications with and can also help find programming errors. When you install the MicroStation SDK, the MicroStation Developer Shell will detect where VisualStudio is installed and run the vsvars32.bat file to set the correct paths and environment variables. Once the basic environment is established, the next thing to consider is actual development. For new applications, the development process is somewhat similar to traditional MDL application development. The main source code file name should be in the form FileName.cpp, where the the .cpp extension is associated to the Visual C/C++ compiler. Other important points include:
Private MdlCommandName commandName[] = { {AddData, "AddData"}, {ReviewData, "ReviewData"}, 0 }; Private MdlCommandNumber commandNumber[] = { {templateapp_command_test, CMD_TEMPLATEAPP_TEST }, {templateapp_command_sctest, CMD_TEMPLATEAPP_SCTEST }, 0 }; /* **//** * @description Main entry point for the example. * @param argc IN Number of arguments passed in argv * @param *argv[] IN Array of pointers to arguments * @return SUCCESS * @remarks + + + ~-+ + + */ extern "C" DLLEXPORT int MdlMain ( int argc, char *argv[] ) { RscFileHandle rscFileH; /* a resource file handle */ mdlResource_openFile (&rscFileH, NULL, 0); mdlSystem_registerCommandNumbers (commandNumber); mdlParse_loadCommandTable (NULL); mdlSystem_setFunction (SYSTEM_UNLOAD_PROGRAM, templateapp_onUnload); mdlState_registerStringIds (STRINGLISTID_CommandNames, STRINGLISTID_CommandNames); return SUCCESS; } To use MicroStation resource-based dialogs and other MDL resource types in native code applications, no special code handling is necessary. However, since traditional MicroStation applications are a combination of code and resources, you need to build at least one MDL resource of type DLLMDLAPP to allow MicroStation to load the native code application as a MicroStation application. This resource type contains the DLL and MDL application names. This allows MicroStation's MDL loader to call the DLL's entry point. The definition looks like this: #define DLLAPPID 1 /* associate app with dll */ DllMdlApp DLLAPPID = { "PlaceLineToolDLL", "PlaceLineToolMDL" } This is typically included in a resource source file that is used to build a resource file, which is then merged into the resulting MA file. To compile or build the native code application, it is still recommended to create and use a MAKE file and the MicroStation Developer Shell delivered with the MicroStation SDK to set the environment accordingly for both MDL and VisualStudio. More information on MAKE files can be found in the MDL Programmer Guide. The MAKE file needs to include rules for building a DLL. These rules are defined by setting a series of macros that are specific to an application as well as including general rules that are defined in dlmcomp.mki and dlmlink.mki. #------------------------------------------------ # Set up to use dlrncornp.rnki and dlrnlink.rnki #------------------------------------------------ dlrnObjs = $(0) $(appNarne) $(oext) DLM_DEST = $(rndlapps) DLM_OBJECT_DEST = $(0) DLM_LIBDEF_SRC = $(baseDir) DLM_NAME = $(appNarne) DLM_ENTRY_NAME = dllentry DLM_RESL_NAME = fileresl DLM_OBJECT_FILES = $(dlrnObjs) DLM_NO_DLS = 1 # Used DLLEXPORT in .c file DLM_NO_DEF = 1 DLM_NOENTRY = 1 DLM_NO_SIGN = 1 DLM_LIBRARY_FILES = $(mdlLibs)dgnfileio.lib $ (mdlLibs) tool subs. lib $(mdlLibs)ditemlib.lib #------------------------------------------------ # Compile the source files for the DLM #------------------------------------------------ %include dlmcomp.mki The relationships in the make file need to be added to build the object file from the source file. This is done with the following syntax: $(o)$(appName)$(oext): $(baseDir)$(appName) .cpp \ $(privateInc)$(appName).h \ $ (genSrc) $ (appName)cmd.h The target output file are identified to the left of the colon and the dependent input files are identified to the right. The final output of the build process will be two files: The first a DLL and the second an MA file that contains the resources for the application. #----------------------------------------------------------------------- # Merge Objects into one file # ~-------------- $(mdIApps)$(appName).ma : $(appRscs) $(msg) >$(0)make.opt -o$@ $(appRscs) < $(RLibCmd) @$(o)make.opt ~time Another important detail that application developers need to be aware of is memory allocation routines. If memory is shared between native code and MDL code, then it is very important to use dlmSystem_... wrappers for the standard functions. By using dlmSystem_... functions, memory allocations are associated with the application, so when the application is unloaded the allocated memory can be cleaned up properly. Also, memory can be shared safely with MDL code. Migrating existing applications to native code requires some minor modification to source code. Before starting this process, it is recommended that you clean out the existing application and its intermediate files. This can be done using the bmake -aD command or by simply deleting the MA file and all the files in ..\mdl\objects\, ..\mdl\rscObjects\, ..\mdl\reqObjects\, and other similar locations. You can also force the bmake process to build MC to OBJ files and then use the VisualStudio compiler. Next, you need to add the DLLMDLAPP resource to the application. Since a command table resource is in most applications, the most logical place to add this resource definition is usually in the file that contains the command table definition. This allows the DLLMDLAPP resource addition without modification to the MKE file. Next, add the MdlMain definition to the source code. This is necessary to provide an entry point to the application. In MdlMain, the application needs to have the MdlCommandNumber and/or MdlCommandName arrays and the corresponding calls to mdlSystem_registerCommandName and/or mdlSystem_RegisterCommandNumber functions. The next step is to change the memory allocation routines to use the dlmSystem_... functions to integrate with MicroStation's memory allocation. There may be some other minor housekeeping chores necessary, since things like NULL in native code do not equate to 0 as they do in MDL. Once the application is built, the next task to consider is debugging. To debug native code applications, you need to attach to the ustation.exe process to the debugger. There are a several ways to do this. One is to start MicroStation in the debugger by using the following command line from the MicroStation Development Shell: msdev %ms%\ustation.exe This starts the Developer Studio IDE with MicroStation loaded. You can then set the debug breaks at the functions in your application. The alternate method is to start MicroStation and then start your debugger, in this example Developer Studio (msdev). Then inside Developer Studio attach to the ustation.exe process from the Build > Start Debug > Attach to Process menu item. From this list, select the ustation.exe process and then add your DLL as an additional DLL to debug. From the Project > Settings menu, select the first tab then in the combo box, select additional DLLs. Then in the main section, select the DLL to debug. Once MicroStation is running in the debug environment, you can set break points on your functions. One tip is to use the first method of starting MicroStation under the debugger and then setting a break point at MdlMain to catch your application at load time. For a more brute force method or when an InitApps is involved, you can use the following code line in your application: __asm int 3; This will cause the application to halt at this instruction, but it can only be used when the application is being actively debugged. There are numerous reasons why MDL developers should focus on developing native code applications -- the C++ compiler and debugging environment are just two of the benefits. More documentation, articles, and examples will be provided in the future regarding this topic.
Private MdlCommandName commandName[] = { {AddData, "AddData"}, {ReviewData, "ReviewData"}, 0 }; Private MdlCommandNumber commandNumber[] = { {templateapp_command_test, CMD_TEMPLATEAPP_TEST }, {templateapp_command_sctest, CMD_TEMPLATEAPP_SCTEST }, 0 }; /* **//** * @description Main entry point for the example. * @param argc IN Number of arguments passed in argv * @param *argv[] IN Array of pointers to arguments * @return SUCCESS * @remarks + + + ~-+ + + */ extern "C" DLLEXPORT int MdlMain ( int argc, char *argv[] ) { RscFileHandle rscFileH; /* a resource file handle */ mdlResource_openFile (&rscFileH, NULL, 0); mdlSystem_registerCommandNumbers (commandNumber); mdlParse_loadCommandTable (NULL); mdlSystem_setFunction (SYSTEM_UNLOAD_PROGRAM, templateapp_onUnload); mdlState_registerStringIds (STRINGLISTID_CommandNames, STRINGLISTID_CommandNames); return SUCCESS; }
#define DLLAPPID 1 /* associate app with dll */ DllMdlApp DLLAPPID = { "PlaceLineToolDLL", "PlaceLineToolMDL" }
#------------------------------------------------ # Set up to use dlrncornp.rnki and dlrnlink.rnki #------------------------------------------------ dlrnObjs = $(0) $(appNarne) $(oext) DLM_DEST = $(rndlapps) DLM_OBJECT_DEST = $(0) DLM_LIBDEF_SRC = $(baseDir) DLM_NAME = $(appNarne) DLM_ENTRY_NAME = dllentry DLM_RESL_NAME = fileresl DLM_OBJECT_FILES = $(dlrnObjs) DLM_NO_DLS = 1 # Used DLLEXPORT in .c file DLM_NO_DEF = 1 DLM_NOENTRY = 1 DLM_NO_SIGN = 1 DLM_LIBRARY_FILES = $(mdlLibs)dgnfileio.lib $ (mdlLibs) tool subs. lib $(mdlLibs)ditemlib.lib #------------------------------------------------ # Compile the source files for the DLM #------------------------------------------------ %include dlmcomp.mki
$(o)$(appName)$(oext): $(baseDir)$(appName) .cpp \ $(privateInc)$(appName).h \ $ (genSrc) $ (appName)cmd.h
#----------------------------------------------------------------------- # Merge Objects into one file # ~-------------- $(mdIApps)$(appName).ma : $(appRscs) $(msg) >$(0)make.opt -o$@ $(appRscs) < $(RLibCmd) @$(o)make.opt ~time
msdev %ms%\ustation.exe
__asm int 3;