Developing with the Unmanaged SDK


Unlike working with the managed SDK, in the unmanaged SDK there won’t be a Visual Studio project to simply open and run.

Editing an Unmanaged Project

Unlike managed projects, unmanaged projects don’t have an associated Visual Studio solution. To edit a project, open the files in a text editor, or open the entire containing folder in Visual Studio or Visual Studio Code. While editing is flexible, unmanaged projects must be build in the SDK Developer Shell command prompt using the bmake command.

Required Project Files

There are a few files that are required to make an unmanaged project work. To illustrate this, I’ll go over the files included in the example. The specific filenames will be different for different projects.

UnmanagedSDKExample.cpp & UnmanagedSDKExample.h

The project needs an entry point, so it will need at least one main .cpp and .h file to serve this function, though you can add more to better organize your project.

The main .cpp file for the project (in this case UnmanagedSDKExample.cpp) must contain the MdlMain() function:

extern "C" DLLEXPORT void MdlMain(int argc, WCharCP argv[])
    {
    MdlCommandNumber commandNumbers [] =
        {
			{ (CmdHandler)unmanagedSDKExample_HorizontalAlignmentReport,    CMD_UNMANAGEDSDKEXAMPLE_HORIZONTALALIGNMENTREPORT },
            0
        };

    /* Register commands */
    mdlSystem_registerCommandNumbers (commandNumbers);
    mdlParse_loadCommandTable (NULL);
    }

The important part of this function is on line 21 of the file – this line maps the function which implements the command (underlined in green) to the command itself (underlined in blue). Defining the command (blue) will be covered later.

The important part of this line is that the implementation function is defined and accessible by the MdlMain() function – it should be defined in a public scope. In this case, the function is defined in HorizontalAlignmentReporter.cpp:

Public void unmanagedSDKExample_HorizontalAlignmentReport(WCharCP unparsed)
{
    HorizontalAlignmentReporter *hRep = new HorizontalAlignmentReporter();
    hRep->GenerateReport(unparsed);
    delete hRep;
}

Also note the arguments passed to these functions – MdlMain() takes an int and WCharCP array, and the command implementation function takes a WCharCP. These are required and will need to be passed to any similar functions you implement in your own projects.

UnmanagedSDKExampleCmd.r

This file creates the command table for the add-in. It essentially serves the same purpose as the CommandTable.xml file in the managed example. There a few relevant parts in this file that will need to be updated for a custom project.

#define  DLLAPP_UNMANAGEDSDKEXAMPLE        1

DllMdlApp DLLAPP_UNMANAGEDSDKEXAMPLE =
    {
    L"UNMANAGEDSDKEXAMPLE",  L"unmanagedsdkexample"  // taskid, dllName
    }

In the image above, the sections underlined in red change depending on the current project. The code above registers the custom project with Microstation so that it can be loaded and called as a key-in command from within OpenRoads Designer.

The next relevant section defines the command table for the key-in. As discussed previously, each key-in command is represented by a hierarchy of commands that can be called (see CommandTable.xml under the Managed SDK Example’s Required Project Files section for a further explanation of this).

In an unmanaged project, this command table is defined as follows:

CommandTable   CT_MAIN =
{
    { 1,    CT_REPORT,  SHOW,  REQ,    "unmanagedsdkexample"   }
};

CommandTable CT_REPORT =
{
    { 1,    CT_NONE,    INHERIT,    NONE,   "HORIZONTALALIGNMENTREPORT",    },
};

In this example, there is a main key-in command table (CT_MAIN) and a key-in sub-table for the horizontal alignment report (CT_REPORT). This means the key-in for this example command is:

UNMANAGEDSDKEXAMPLE > HORIZONTALALIGNMENTREPORT

The first section defines the UnmanagedSDKExample command and passes CT_REPORT as a table of sub-commands. Since HORIZONTALALIGNMENTREPORT is the only command, its sub-table is set as CT_NONE.

Each command definition has a few parameters:

You can also define multiple levels of command hierarchies. Here’s an example taken from another unmanaged project:

CommandTable CT_MAIN =
{
    {  1, CT_LISTBOX, INPUT, HID, "LISTBOX" },
};
CommandTable CT_LISTBOX =
{
    {  1, CT_OPEN, INHERIT, NONE, "OPEN" },
};
CommandTable CT_OPEN =
{
   {  1, CT_NONE, INHERIT, NONE, "LISTICON" },
   {  2, CT_NONE, INHERIT, NONE, "MULTILIST" },
   {  3, CT_NONE, INHERIT, NONE, "SASH" },
   {  4, CT_NONE, INHERIT, NONE, "COLORS" },
};

In this case, the user has defined the LISTBOX > OPEN command, which can be followed by any of the four defined sub-commands.

UnmanagedSDKExample.mke

The .mke file for your project is what the bmake command uses to compile your code into an OpenRoads key-in. It’s easiest to simply edit an example. There are only two parts of the .mke file that need to be tailored to the specific project being compiled.

First, specify the appName. This variable is used throughout the .mke file to name any compiled objects (like .dll and .pdb files) and to locate the main files for compiling. This means that to use this .mke file, the appName must match the name of your project’s main .cpp, .h and .r files.  See below:

21    appName         = UnmanagedSDKExample

You can add additional files and name them whatever you’d like. Those files are added for compilation by editing the following section:

93 $(o)$(appName)$(oext)    : $(baseDir)$(appName).cpp  $(o)$(appName)cmd.h

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

97 $(o)Helpers$(oext) : $(baseDir)Helpers.cpp $(baseDir)Helpers.h

In the code above, the first line compiles the main application object. The next two lines compile the additional files into their own objects: HorizontalAlignmentReporter.cpp, Helpers.cpp and Helpers.h. This is the format you should use to add and compile source files in an unmanaged project.

Debugging an Unmanaged Project

Compared to a managed project, debugging an unmanaged project can be tricky. The first step is to compile your project inside the SDK Developer Shell. Once compiled, you can open Visual Studio and follow these steps to debug:

1. Open the executable file for your installed version of Open Roads Designer.

Inside Visual Studio, you can open an executable file just like you can open a solution file – using the dialog under File > Open > Project/Solution. From there, make sure that the file type you’re browsing for is All Project Files:

Now, navigate to your installation of ORD. For example, most users have it installed under their program files:

C:\Program Files\Bentley\OpenRoads Designer CONNECT Edition\OpenRoadsDesigner

In that folder is the OpenRoadsDesigner.exe file, which you should be able to select to open in Visual Studio.

2. Open the files compiled for your project.

When you compiled your project in the SDK Developer Shell, the compiler took the source files specified in your .mke file and compiled them into objects, putting them inside of your installation of ORD. Therefore, make sure any files you want to debug are the files used in that compilation.

3. Start OpenRoads Designer from Visual Studio

Since you loaded the OpenRoadsDesigner.exe file, if you run the “Start” command.

4. Load your command.

From there, you can load your command like normal using the mdl load key-in. You can then place breakpoints in your source code and debug. Note that the break points won’t be loaded until you load your key-in.