Calling C/C++ Functions in Add-ins


In the First Chapter, we brought up that the Add-ins development method on the MSTN CE version adds a new programming framework that is almost "parallel" to the NativeCode architecture. "Almost" is used here because C# and C/C++ are two completely different languages. Although there are many similarities, it is impossible to achieve complete parallelism. So if we choose the Add-ins development method using C#, we have to find a way to call the C/C++ interface in Addins by ourselvesThere are two ways to achieve this, one is to call the function exported by NativeCode through C# 's PInvoke, and the other is to directly implement mixed programming of C# and C/C++ through C++/CLIThis chapter briefly introduces PInvoke, and the next chapter introduces C++/CLI.

Level is a way of grouping elements in MSTN. By placing the elements under different levels, the elements can be grouped and managed. Both Addins and NativeCode have interfaces to get the Level name in the current Dgn file. But when you use the interface in Addins to get the Level name, you will find that some Level names cannot be obtained. But all can be obtained under NativeCode. The reason is that the Level in the Dgn file is composed of two parts, one is the Level stored in the current Dgn file, and the other is the Level loaded from DgnLib under WorkSpaceThe name of these two parts of the interface under NativeCode can be obtained, while under Addins, only the name of the level in the current Dgn file can be obtained. In this chapter, we will use PInvoke on the C# side to call a function encapsulated on the NativeCode side to get the names of all levels in the Dgn file.

This article mainly introduces the development of Addins, so how to get the name of Level on NativeCode will not be explained in detail. On the NativeCode side, we exported two functions GetDgnlibLevelNames and ReleaseDgnlibLevelNamesGetDgnlibLevelNames obtains the name of the Level, and ReleaseDgnlibLevelNames is responsible for releasing the dynamically requested memory. We will encapsulate and call these two functions in Add-ins. Please download the relevant native code from here and create a dll and copy that dll t ...\Bentley\ MicroStation CONNECT Edition\ MicroStation\ Mdlapps.

Let us implement how to call these two functions in C# Add-in:

  1. Open the commands.xml file, add the command csAddins DemoForm ShowLevelNames, and specify its processing function as csAddins.DemoForm.ShowLevelNamesIf you are not familiar with XML format command list files, please refer to the related topics in Chapter 4.
  2. Select the Project > csAddins Properties, switch to Build, and select the "Allow Unsafe Code" check box, as shown in the figure below.

3.Open the DemoForm.cs file and add the following using statement at the beginning of the code.

using System.Runtime.InteropServices; 
using Bentley.DgnPlatformNET;
using Bentley.MstnPlatformNET;

4. Turn to the end of the file and add the command processing function ShowLevelNames and the DllImport attribute declaration required to call the external function. The final source code is as follows.

unsafe public static void ShowLevelNames(string unparsed)
        {
            List<string> namesList = new List<string>();
            LevelHandleCollection lvlHanCol = Session.Instance.GetActiveDgnFile().GetLevelCache().GetHandles();
            foreach (LevelHandle lvlHan in lvlHanCol)
            {
                namesList.Add(lvlHan.Name);
            }
            int namesCnt = 0;
            void** namesvpp = GetDgnlibLevelNames(ref namesCnt);
            IntPtr ptr = new IntPtr(namesvpp);
            for (int i = 0; i < namesCnt; i++)
            {
                IntPtr ptr1 = new IntPtr(ptr.ToInt64() + 8 * i);
                string lvlName = Marshal.PtrToStringUni(new IntPtr(*(void**)ptr1.ToPointer()));
                namesList.Add(lvlName);

            }
            ReleaseDgnlibLevelNames(namesvpp, namesCnt);
            foreach(string lvlName in namesList)
            {
                MessageCenter.Instance.ShowInfoMessage(lvlName, lvlName, false);
            }
        }

        [DllImport("SampleNative.dll")]
        public static unsafe extern void** GetDgnlibLevelNames(ref int namesCnt);


        [DllImport("SampleNative.dll")]
        public static unsafe extern void ReleaseDgnlibLevelNames(void** namesPP, int namesCnt);

5. Rebuild csAddins, then start Mstn and open a dgn file, type mdl load csAddins to load csAddins, and then type csaddins demoform showlevelnames, in the message center of Mstn (double-click the message bar at the bottom of Mstn to open), it will display the names of all levels in the current dgn file , Including the Level loaded from Dgnlib. The following is the result of running on my machine.

The parameters and return value types of the two functions we encapsulated here are of very basic types. The parameter types in the actual development process can be very complicated, for example, the parameter types can be C++ classes or structures. When such a function is encapsulated, we need to declare and define a type that is consistent with the class or structure of the C++ side in the binary layout on the Addins side, and then call the function from the NativeCode side through the DllImport attribute declaration. How to declare and define in C# types that are consistent with the class or structure in C++ is beyond the scope of this article. The readers are encouraged to find appropriate resources from the internet.

Prev:[[Responding to MicroStation Events]]Next:[[Writing Add-ins in C++/CLI]]