第九章、用C++/CLI编写AddinsChapter 9. Writing Addins using C++/CLI
从上一章我们能够看到,要调用MDL API函数需要通过DllImport属性对其进行声明。好在我们所演示的mdlMesh_newPolyfaceFromXYTriangulation函数还算简单,而且要生成一个网格曲面仅调用这一个函数即可。假如您要进行智能实体的编程可就没有这么幸运了,因为它往往需要调用多个MDL API函数才能实现一个功能。如果还对每个MDL函数进行DllImport属性声明,一则是比较繁琐,再者就是难度也不小。好在微软为我们提供了功能强大的混合程序集编程语言——C++/CLI。当我们改用C++/CLI写我们的程序集时,既可以调用.NET的功能也可以像本机代码C/C++那样直接调用MDL函数。下面就让我们来一步步编写一个C++/CLI的Addin程序,其中演示了绘制上一章的网格曲面以及绘制一个智能实体。From the last chapter, maybe you have found that in order to call the MDL API function we have to declare it by using DllImport attribute. Fortunately, the function mdlMesh_newPolyfaceFromXYTriangulation is simple and we just need one such function to create a mesh surface. But if you want to create smart solid by programming, you will have no luck because it normally needs to call several MDL functions. In this case, it will be cumbersome and difficult to declare every function by DllImport attributes. Luckily Microsoft provides us a powerful mixed assembly programming language, it is C++/CLI. When we change to write our assembly with C++/CLI, it is possible to use .NET functionality and to call MDL functions directly. Now, let’s write an Addin application by using C++/CLI. We will demonstrate creating a mesh surface and a smart solid.
1. 在VS中打开我们的csAddins解决方案,选菜单File > New > Project打开新建项目窗体,按下图所示在现有解决方案中增加一个新的项目cppAddins。注意新项目的类型是Visual C++/CLR下的Class Library,且Solution的选项要选择为Add to solution。1. Open our csAddins solution in VS and open the Create New Project form by selecting menu File > New > Project. Then add a new project cppAddins according to the below picture. Please note, the type of new project is Class Library under the category Visual C++/CLR and the Solution option should be selected as Add to solution.
2. 选中解决方案csAddins(而不是项目csAddins)后按键盘的<F2>功能键,随后将csAddins改名为mstnAddins。退出VS,到Windows资源管理器中将原有的文件夹csAddins也改名为mstnAddins。重新进入VS,打开新的mstnAddins解决方案。现在我们的解决方案中有了两个项目,如下图所示:2. Select solution csAddins (not the project csAddins), then press the function key <F2> to rename the csAddins to mstnAddins. Exit VS and rename the folder name csAddins to mstnAddins in Windows explorer. Restart VS, open the new mstnAddins solution. Now you should see two projects cppAddins and csAddins in the solution mstnAddins shown as below:
3. 请确保您的系统环境变量中有对MS的正确定义,因为我们下面的设置都要依赖于这个环境变量。该环境变量应该指向您MstnV8iSS2的MicroStation安装位置。我机器的情况如下图所示。如果您的机器中还没有这个设置,请先退出VS,到桌面“我的电脑”的属性中找到下图所示位置新增该环境变量,然后再重新进入VS。之所以要退出VS并重新进入是因为VS只有在启动时才会查询环境变量。3. Make sure you have a correct MS variable definition in you system environment because our following settings will heavily depend on this environment variable. This variable should point to the installation location of your MstnV8iSS2. The below picture shows the case in my box. If you haven’t this setting, please exit VS, add this new environment variable followed by the below picture which you can begin from the property of "My Computer" and then restart VS. Why we need to exit and restart VS? Because VS searches system environment variables only at its starting time.
4. 下面来设置C++项目的一些默认路径,这一步骤仅需做一次。所有C++项目时都能自动继承这些设置。选菜单View > Property Manager打开属性管理器对话框,依次展开cppAddins和Release|Win32(或Debug|Win32),双击Microsoft.Cpp.Win32.user进一步打开默认属性页对话框。在默认属性页对话框中左侧选中VC++ Directories,右侧分别设置Include Directories、Reference Directories和Library Directories如下图所示。4. Next we will set the default path of C++ project. This step is needed to do only once and all C++ projects will inherit these settings. Open property manager by selecting menu View > Property Manager, expand cppAddins and Release|Win32 (or Debug|Win32), then double click Microsoft.Cpp.Win32.user to open the project default property page dialogbox. Select the VC++ Directories on the left pane of this dialogbox, and then set the Include Directories, Reference Directories and Library Directories on the right pane like the following picture shown.
5. 设置cppAddins项目的属性。右击cppAddins并在弹出的菜单中选择Properties打开项目属性对话框。在General分类下将Output Directory设置为$(MS)mdlapps\,这样使得生成的cppAddins位于Mstn的mdlapps文件夹下。在Build Events的Post-Build Event下将Command Line设置为$(MS)assemblies\UstnXOM ValidateAddIn $(TargetPath) (当然,如果您安装了Mstn SDK的话,这里也可以写成$(MS)mdl\bin\UstnXOM ValidateAddIn $(TargetPath))。该设置的目的是为了在项目生成完成后校验程序集的完整性,详细说明参见第四章的相关介绍。5. Set the project property of cppAddins. Open the project property dialogbox by right clicking the cppAddins and selecting the Properties menu item on the popup menu. Set Output Directory to $(MS)mdlapps\ under the category General. This makes the cppAddins.dll is created under the mdlapps folder of Mstn. Set Command Line to $(MS)assemblies\UstnXOM ValidateAddIn $(TargetPath) under the Post-Build Event of Build Events (Certainly, you can set Command Line to $(MS)mdl\bin\UstnXOM ValidateAddIn $(TargetPath) if you have installed Mstn SDK). This setting makes the assembly validation check after finishing the project build. Please refer to chapter 4 for the detailed description of assembly’s validation.
6. 在Window资源管理器中将csAddins项目中的commands.xml文件复制一份到cppAddins项目下。然后回到VS中,将文件commands.xml添加到cppAddins项目中的Resource Files下。在该文件的属性对话框中,设置其Item Type属性为Compiled Managed Resource。这一步非常重要,能将commands.xml文件作为资源嵌入到最终的程序集cppAddins.dll中。6. Copy file commands.xml from project csAddins to cppAddins under Windows explorer. Back to VS, add commands.xml under the Resource Files of cppAddins. Open this file’s property page, set its Item Type to Compiled Managed Resource. This is very important because it can make commands.xml embedded into the final assembly cppAddins.dll as resource.
7. 双击commands.xml文件打开并编辑它。最终构成两个命令cppAddins CreateElement Mesh和cppAddins CreateElement Solid。完成后的文件内容如下所示:7. Double click commands.xml to open it, and then edit it. The two commands we created are cppAddins CreateElement Mesh and cppAddins CreateElement Solid. The final edition of this file is as below:
<?xml version="1.0" encoding="utf-8" ?><KeyinTree xmlns="http://www.bentley.com/schemas/1.0/MicroStation/AddIn/KeyinTree.xsd"> <RootKeyinTable ID="root"> <Keyword SubtableRef="CreateElement" CommandClass="MacroCommand" CommandWord="cppAddins" > <Options Required="true"/> </Keyword> </RootKeyinTable> <SubKeyinTables> <KeyinTable ID="CreateElement"> <Keyword SubtableRef="Commands" CommandWord="CreateElement"> <Options Required="true"/> </Keyword> </KeyinTable> <KeyinTable ID="Commands"> <Keyword CommandWord="Mesh"> </Keyword> <Keyword CommandWord="Solid"> </Keyword> </KeyinTable> </SubKeyinTables> <KeyinHandlers> <KeyinHandler Keyin="cppAddins CreateElement Mesh" Function="cppAddins.CreateElement.Mesh"/> <KeyinHandler Keyin="cppAddins CreateElement Solid" Function="cppAddins.CreateElement.Solid"/> </KeyinHandlers></KeyinTree>
<?xml version="1.0" encoding="utf-8" ?><KeyinTree xmlns="http://www.bentley.com/schemas/1.0/MicroStation/AddIn/KeyinTree.xsd"> <RootKeyinTable ID="root"> <Keyword SubtableRef="CreateElement" CommandClass="MacroCommand" CommandWord="cppAddins" > <Options Required="true"/> </Keyword> </RootKeyinTable>
<SubKeyinTables> <KeyinTable ID="CreateElement"> <Keyword SubtableRef="Commands" CommandWord="CreateElement"> <Options Required="true"/> </Keyword> </KeyinTable> <KeyinTable ID="Commands"> <Keyword CommandWord="Mesh"> </Keyword> <Keyword CommandWord="Solid"> </Keyword> </KeyinTable> </SubKeyinTables>
<KeyinHandlers> <KeyinHandler Keyin="cppAddins CreateElement Mesh" Function="cppAddins.CreateElement.Mesh"/> <KeyinHandler Keyin="cppAddins CreateElement Solid" Function="cppAddins.CreateElement.Solid"/> </KeyinHandlers></KeyinTree>
8. 双击Header Files下的stdafx.h文件打开并编辑它,最终的结果如下。为了调用MDL函数,我们定义了winNT并包含了一个头文件(.h)和三个函数定义文件(.fdf)。为了用C++构造Mstn Addin程序集,我们用#using指令引入了五个必要的DLL。同时,我们还用using namespace声明了命名空间std和System::IO。在此需要特别强调的是,我们取消了原有的using namespace System一行。因为如果使用这一声明的话,在MDL函数中大量使用的全局类型定义UInt32会与.NET中的System::UInt32发生冲突。8. Double click the file stdafx.h under Header Files to open it, and then edit it as below. To call MDL functions, we define winNT and include one header file (.h) and three function definition file (.fdf). To construct Mstn Addin assembly with C++, we use #using directive to reference five necessary DLLs. Meantime, we use using namespace declaration to declare namespace std and System::IO. Special emphasis here, we eliminate the line ‘using namespace System’ because it will bring ambiguous symbol error when you try to use UInt32 type in your program. UInt32 is a global type definition in MDL, it conflicts with the System::UInt32 when we omit the System namespace.
#pragma once#define winNT 1#include <MicroStationAPI.h>#include <mselmdsc.fdf>#include <msmdlmesh.fdf>#include <mskisolid.fdf>#using <System.dll>#using <ustation.dll>#using <Bentley.MicroStation.dll>#using <Bentley.MicroStation.Interfaces.1.0.dll>#using <Bentley.General.1.0.dll>using namespace std;using namespace System::IO;
#pragma once#define winNT 1
#include <MicroStationAPI.h>#include <mselmdsc.fdf>#include <msmdlmesh.fdf>#include <mskisolid.fdf>
#using <System.dll>#using <ustation.dll>#using <Bentley.MicroStation.dll>#using <Bentley.MicroStation.Interfaces.1.0.dll>#using <Bentley.General.1.0.dll>
using namespace std;using namespace System::IO;
9. 双击Source Files下的stdafx.cpp文件打开并编辑它,最终结果如下。在该文件中我们仅仅是用杂注comment指明了链接时所用到的几个静态链接库。当然,您也可以在项目属性的Linker下来指定这些静态链接库文件。9. Double click the file stdafx.cpp under Source Files to open it, and then edit is as below. In this file, we use pragma comment to identify the static libraries we need to link. Certainly, you can also specify these library files under the Linker category of project property.
#include "stdafx.h"#pragma comment(lib, "mdlbltin.lib")#pragma comment(lib, "BentleyDgn.lib")#pragma comment(lib, "toolsubs.lib")#pragma comment(lib, "mdllib.lib")#pragma comment(lib, "msbspline.lib")#pragma comment(lib, "kisolid.lib")
#include "stdafx.h"
#pragma comment(lib, "mdlbltin.lib")#pragma comment(lib, "BentleyDgn.lib")#pragma comment(lib, "toolsubs.lib")#pragma comment(lib, "mdllib.lib")#pragma comment(lib, "msbspline.lib")#pragma comment(lib, "kisolid.lib")
10. 双击Source Files下的cppAddins.cpp文件打开并编辑它,最终结果如下。该文件构成了Mstn Addin程序集的骨架,其具体含义可参考我们前面有关用C#语言编写Mstn Addin程序集的介绍。10. Double click the file cppAddins.cpp under Source Files to open it, and then edit is as below. This file is the main framework of Mstn Addin assembly. Please refer to the previous related topics of writing Mstn Addin assembly by using C# to understand their meanings.
#include "stdafx.h"namespace cppAddins{ typedef Bentley::MicroStation::AddIn Base; [Bentley::MicroStation::AddInAttribute(KeyinTree="commands.xml", MdlTaskID="CppAddins")] public ref class MyAddin sealed : public Base { private: MyAddin(System::IntPtr mdlDesc) : Base(mdlDesc) { } public: virtual int Run(array<System::String^>^ commandLine) override { return 0; } };}
namespace cppAddins{ typedef Bentley::MicroStation::AddIn Base; [Bentley::MicroStation::AddInAttribute(KeyinTree="commands.xml", MdlTaskID="CppAddins")] public ref class MyAddin sealed : public Base { private: MyAddin(System::IntPtr mdlDesc) : Base(mdlDesc) { } public: virtual int Run(array<System::String^>^ commandLine) override { return 0; } };}
11. 右击cppAddins项目下的Source Files,在弹出的菜单中选择Add > New Item,然后在新弹出的窗体中选择文件类型为C++ File(.cpp),并输入文件名为CreateElement,点击Ok按钮后会在Source Files下新增一个程序源文件CreateElement.cpp。这是我们的主要工作文件。11. Right click Source Files under the project cppAddins, select Add > New Item in the popup menu, and then select file type is C++ File(.cpp) in the new popup form and input file name CreateElement. Click Ok button to add a new source file CreateElement.cpp under the Source Files. This is our main work file.
12. 在CreateElement.cpp中输入如下内容。需要说明的是:①定义了两个命令处理函数Mesh和Solid分别来处理cppAddins CreateElement Mesh和cppAddins CreateElement Solid两个命令;②在Mesh函数中改用C++STL的vector来动态保存数据文件data-13.asc中的数据点,并用mdlCnv¬_ masterUnitsToUors将接收的数据点坐标从主单位转换为分辨率单位。这是因为MDL函数中默认的数据点单位都是分辨率单位而非像MVBA或MicroStationDGN COM中那样默认数据点单位就是主单位(如果您还不熟悉Mstn中工作单位的概念,请从Mstn的帮助文档中来学习)。③注意,我们在Mesh和Solid函数中都调用不少MDL函数,如mdlCnv¬_ masterUnitsToUors、mdlMesh_newPolyfaceFromXYTriangulation、mdlElmdscr_add以及mdlKISolid_xxx,这些函数都不需要事先用DllImport属性声明,这正是我们改用C++/CLI重写Mstn Addin程序集的最根本原因。④Solid函数中调用了许多的MDL函数来为mdlKISolid_sweepBodyWire函数服务。它首先建立了一条折线作为路径,然后建立了一个圆作为断面。通过断面沿着路径扫掠构成了空间的一条管道,其元素类型是智能实体(type=2)不是普通实体(type=19)。有关KISolid编程的细节描述超出了本博客的范畴,如果您对代码的具体含义不能充分理解的话,请私下和我交流或直接在本博客的注解中发问。12. Input the following contents in CreateElement.cpp. We explain several keypoints here: a) We define two command handlers Mesh and Solid to process command cppAddins CreateElement Mesh and cppAddins CreateElement Solid. b) In Mesh function, we change to use C++STL vector to store datapoints which come from data file data-13.asc dynamically. And we use mdlCnv_ masterUnitsToUors to convert datapoints from master unit to resolution unit because the default unit in MDL function is resolution unit not master unit. (If you aren’t familiar with the concept of working unit please to learn it from the help documentation of Mstn). c) Please note, we use a lot of MDL functions, such as mdlCnv_ masterUnitsToUors, mdlMesh_newPolyfaceFromXYTriangulation, mdlElmdscr_add and mdlKISolid_xxx. We don’t need to declare them with DllImport attributes and this is just the main reason we change to C++/CLI. d) In order to call mdlKISolid_sweepBodyWire, we have to call several extra MDL functions in Solid function. We firstly create a linestring as path, and then create a circle as profile. By sweeping the profile along with the path, we create a 3D pipe which type is smart solid (type=2) not solid (type=19). Detailed explanation of KISolid functions is out of the range of this blog. If you are interested in the code and have some doubts, welcome to contact me or write them directly in the comments of this blog.
#include "stdafx.h"namespace cppAddins{ public ref class CreateElement { public: static void Mesh (System::String^ unparsed) { DPoint3d pt, *pPt = NULL, *xyzArray = NULL; vector <DPoint3d> meshPnts; array<System::Char>^ delimiterChars = { ' ' }; System::String^ myLine; StreamReader^ sr = gcnew StreamReader("d:\\atemp\\data-13.asc"); while (nullptr != (myLine = sr->ReadLine())) { array<System::String^>^ sArray = myLine->Split(delimiterChars); pt.x = mdlCnv_masterUnitsToUors (System::Double::Parse(sArray[0])); pt.y = mdlCnv_masterUnitsToUors (System::Double::Parse(sArray[1])); pt.z = mdlCnv_masterUnitsToUors (System::Double::Parse(sArray[2])); meshPnts.push_back (pt); } sr->Close(); xyzArray = new DPoint3d[meshPnts.size()]; pPt = xyzArray; for (vector<DPoint3d>::iterator iter=meshPnts.begin(); iter != meshPnts.end(); ++iter,++pPt) *pPt = *iter; MSElementDescrP pMeshDescr = NULL; if (SUCCESS == mdlMesh_newPolyfaceFromXYTriangulation(&pMeshDescr, xyzArray, meshPnts.size())) { mdlElmdscr_add (pMeshDescr); mdlElmdscr_freeAll (&pMeshDescr); } delete[] xyzArray; } static void Solid (System::String^ unparsed) { MSElement el; MSElementDescrP pPathDescr = NULL, pProfileDescr = NULL, pSolidDescr = NULL; KIBODY *pPathBody = NULL, *pProfileBody = NULL; Transform fwdBodyTransform, tmpBodyTransform, toolTransform; RotMatrix rMatrix; DPoint3d pts[4] = {{0,0,0}, {5000,0,0}, {5000,5000,0}, {5000,5000,5000}}; DVec3d zVec; mdlLineString_create (&el, NULL, pts, 4); mdlElmdscr_new (&pPathDescr, NULL, &el); mdlVec_subtractDPoint3dDPoint3d (&zVec, &pts[1], &pts[0]); mdlVec_normalizeDVec3d (&zVec, &zVec); mdlRMatrix_fromNormalVector (&rMatrix, &zVec); mdlRMatrix_getInverse (&rMatrix, &rMatrix); mdlEllipse_create (&el, NULL, pts, 200, 200, &rMatrix, 0); mdlElmdscr_new (&pProfileDescr, NULL, &el); if (SUCCESS == mdlKISolid_elementToBody2 (&pPathBody, &fwdBodyTransform, pPathDescr, ACTIVEMODEL, 1L, FALSE) && SUCCESS == mdlKISolid_elementToBody2 (&pProfileBody, &tmpBodyTransform, pProfileDescr, ACTIVEMODEL, 1L, FALSE) && NULL != pPathBody && NULL != pProfileBody) { Transform invBodyTransform; mdlTMatrix_getInverse (&invBodyTransform, &fwdBodyTransform); mdlTMatrix_multiply (&toolTransform, &invBodyTransform, &tmpBodyTransform); mdlKISolid_applyTransform (pProfileBody, &toolTransform); if (SUCCESS == mdlKISolid_sweepBodyWire (&pProfileBody, pPathBody, NULL, 0, true, ACTIVEMODEL)) { if (SUCCESS == mdlKISolid_bodyToElement (&pSolidDescr, pProfileBody, -1, -1, NULL, ACTIVEMODEL)) { mdlKISolid_beginCurrTrans (ACTIVEMODEL); mdlElmdscr_transform (pSolidDescr, &fwdBodyTransform); mdlKISolid_endCurrTrans (); mdlElmdscr_add (pSolidDescr); mdlElmdscr_freeAll (&pSolidDescr); } } } if (NULL != pProfileBody) mdlKISolid_freeBody (pProfileBody); if (NULL != pPathBody) mdlKISolid_freeBody (pPathBody); if (NULL != pPathDescr) mdlElmdscr_freeAll (&pPathDescr); if (NULL != pProfileDescr) mdlElmdscr_freeAll (&pProfileDescr); } };};
namespace cppAddins{ public ref class CreateElement { public: static void Mesh (System::String^ unparsed) { DPoint3d pt, *pPt = NULL, *xyzArray = NULL; vector <DPoint3d> meshPnts; array<System::Char>^ delimiterChars = { ' ' }; System::String^ myLine; StreamReader^ sr = gcnew StreamReader("d:\\atemp\\data-13.asc"); while (nullptr != (myLine = sr->ReadLine())) { array<System::String^>^ sArray = myLine->Split(delimiterChars); pt.x = mdlCnv_masterUnitsToUors (System::Double::Parse(sArray[0])); pt.y = mdlCnv_masterUnitsToUors (System::Double::Parse(sArray[1])); pt.z = mdlCnv_masterUnitsToUors (System::Double::Parse(sArray[2])); meshPnts.push_back (pt); } sr->Close(); xyzArray = new DPoint3d[meshPnts.size()]; pPt = xyzArray; for (vector<DPoint3d>::iterator iter=meshPnts.begin(); iter != meshPnts.end(); ++iter,++pPt) *pPt = *iter; MSElementDescrP pMeshDescr = NULL; if (SUCCESS == mdlMesh_newPolyfaceFromXYTriangulation(&pMeshDescr, xyzArray, meshPnts.size())) { mdlElmdscr_add (pMeshDescr); mdlElmdscr_freeAll (&pMeshDescr); } delete[] xyzArray; } static void Solid (System::String^ unparsed) { MSElement el; MSElementDescrP pPathDescr = NULL, pProfileDescr = NULL, pSolidDescr = NULL; KIBODY *pPathBody = NULL, *pProfileBody = NULL; Transform fwdBodyTransform, tmpBodyTransform, toolTransform; RotMatrix rMatrix; DPoint3d pts[4] = {{0,0,0}, {5000,0,0}, {5000,5000,0}, {5000,5000,5000}}; DVec3d zVec;
mdlLineString_create (&el, NULL, pts, 4); mdlElmdscr_new (&pPathDescr, NULL, &el);
mdlVec_subtractDPoint3dDPoint3d (&zVec, &pts[1], &pts[0]); mdlVec_normalizeDVec3d (&zVec, &zVec); mdlRMatrix_fromNormalVector (&rMatrix, &zVec); mdlRMatrix_getInverse (&rMatrix, &rMatrix); mdlEllipse_create (&el, NULL, pts, 200, 200, &rMatrix, 0); mdlElmdscr_new (&pProfileDescr, NULL, &el); if (SUCCESS == mdlKISolid_elementToBody2 (&pPathBody, &fwdBodyTransform, pPathDescr, ACTIVEMODEL, 1L, FALSE) && SUCCESS == mdlKISolid_elementToBody2 (&pProfileBody, &tmpBodyTransform, pProfileDescr, ACTIVEMODEL, 1L, FALSE) && NULL != pPathBody && NULL != pProfileBody) { Transform invBodyTransform; mdlTMatrix_getInverse (&invBodyTransform, &fwdBodyTransform); mdlTMatrix_multiply (&toolTransform, &invBodyTransform, &tmpBodyTransform); mdlKISolid_applyTransform (pProfileBody, &toolTransform); if (SUCCESS == mdlKISolid_sweepBodyWire (&pProfileBody, pPathBody, NULL, 0, true, ACTIVEMODEL)) { if (SUCCESS == mdlKISolid_bodyToElement (&pSolidDescr, pProfileBody, -1, -1, NULL, ACTIVEMODEL)) { mdlKISolid_beginCurrTrans (ACTIVEMODEL); mdlElmdscr_transform (pSolidDescr, &fwdBodyTransform); mdlKISolid_endCurrTrans (); mdlElmdscr_add (pSolidDescr); mdlElmdscr_freeAll (&pSolidDescr); } } } if (NULL != pProfileBody) mdlKISolid_freeBody (pProfileBody); if (NULL != pPathBody) mdlKISolid_freeBody (pPathBody); if (NULL != pPathDescr) mdlElmdscr_freeAll (&pPathDescr); if (NULL != pProfileDescr) mdlElmdscr_freeAll (&pProfileDescr); } };};
13. 生成并测试cppAddins程序集。右击cppAddins项目,在弹出的菜单中选择Build来生成cppAddins.dll。或者更简单地,直接点击Build工具栏中的第一个图标来生成cppAddins.dll。当成功地生成了cppAddins程序集后,启动Mstn并打开一个具有三维模型的DGN文件,键入MDL LOAD cppAddins并回车装载cppAddins程序集,再次键入cppAddins CreateElement Mesh并回车就能绘制出和上一章一模一样的网格曲面。键入cppAddins CreateElement Solid并回车将能生成一个三维的管道,如下图所示:13. Build and test cppAddins assembly. Right click cppAddins project and select Build menu item in the popup menu to create cppAddins.dll. Or you can simple click the first icon tool in the Build toolbox to create cppAddins.dll. After you have successfully created the assembly, you can start Mstn and open a DGN with 3D model. Next, keyin MDL LOAD cppAddins to load cppAddins assembly. And then, keyin cppAddins CreateElement Mesh to draw a mesh surface which is same as the surface drawn in last chapter. Finally, keyin cppAddins CreateElement Solid to draw a 3D pipe shown as below pictures.
至此,我们的《一步步学习MicroStation Addins》编程系列博客可以收笔了。完整的mstnAddins解决方案源文件下载链接如下。非常欢迎大家批评指正,非常欢迎与他人分享该博客。Thus far, we finished our ‘Learning MicroStation Addins Step by Step’ series. The final source code of solution mstnAddins is as below. Warmly welcome to identify my mistakes and warmly welcome to share this blog to other people.mstnAddins.zip