Bentley Communities
Bentley Communities
  • Site
  • User
  • Site
  • Search
  • User
  • Welcome
  • Products
  • Support
  • About
  • More
  • Cancel
中国BDN社区
  • Welcome to Bentley Communities
  • Bentley's Communities
  • 中国BDN社区
  • Cancel
中国BDN社区
中国BDN社区-威客 第九章、用C++/CLI编写Addins
    • Sign In
    • 中国BDN社区-威客
    • +iTwin.js编程
    • +MicroStation CONNECT版SDK的新变化
    • +MicroStation编程技巧
    • +OpenRoads中国版平台开发指南
    • +ORD SDK二次开发编程
    • +一步步学习ABD-CE开发
    • -一步步学习MicroStation CE Addin开发
      • 第0章、介绍与必备条件
      • 第1章、建立一个最简单的Addin应用程序
      • 第2章、在Addin中创建元素
      • 第3章、运行并调试Addin
      • 第4章、给Addins添加命令
      • 第5章、给Addins添加Windows窗体
      • 第6章、用DgnPrimitiveTool和DgnElementSetTool实现交互式命令
      • 第7章、响应MicroStation事件
      • 第8章、在Addins中调用C/C++函数
      • 第九章、用C++/CLI编写Addins
    • 一步步学习MicroStation CE MDL开发
    • +一步步学习ProjectWise编程
    • 中国优先社区二次开发精华帖汇总
    • +学习Microstation交互式工具开发
    • +过期帖,留存仅供参考
    • +非Bentley社区分享的文章
    • C#、C/C++相关的编程知识汇总

     
     Questions about this article, topic, or product? Click here. 

    第九章、用C++/CLI编写Addins

    上一章中简单演示了如何在Addins端封装调用NativeCode端导出的函数,虽然通过PInvoke可以调用到大部分NativeCode端导出的函数,但是C/C++中一些特殊的函数是无法通过PInvoke进行封装的,例如构造函数,本章演示的例子就属于这种情况。在第零章中已经提到过CE版本上Addins中有两套开发框架,在第二章中简单演示了分别在两套开发框架下如何创建元素。在实际开发过程中,我们有时候会遇到要将元素转换到另一套开发框架下的情况。这个时候您会发现,Addins中并没有接口去完成这样的功能。但是如果您仔细研究的话,会发现两套开发框架下的Element对象都留有接口能获取到NativeCode端与元素构造相关的对象,以及从NativeCode端元素相关的对象构造其对象实例。COM框架下的Element对象有MdlElementRef和MdlElementDescrP两个成员函数可以获取到NativeCode端元素的ElementRefP和MSElementDescrP指针。Bentley.Interop.MicroStationDGN.Application 下的MdlCreateElementFromElementDescrP成员函数可以从MSElementDescrP构造出COM框架下的Element。而新的开发框架下的Element有一个ElementHandle的属性能获取到NativeCode端ElementHandle的指针。其有一个保护类型的成员函数InitializeFromElementHandle可以从NativeCode端ElementHandle的指针构造出其对象实例。在NativeCode端,ElementHandle的几个重载构造函数中有接收ElementRefP或者MSElementDescrP为参数的构造函数,且ElementHandle也有获取ElementRefP和MSElementDescrP的成员函数。所以以NativeCode的对象ElementHandle为桥梁我们就可以完成两套编程框架下的元素相互转换。如果我们用PInvoke的技术来实现的话,就需要在NativeCode端导出这样两个转换函数,一个以ElementRefP或者MSElementDescrP指针为参数以ElementHandleP指针为返回值的转换函数实现从COM框架下的Element到新的编程框架下的Element的转换。另外一个是以ElementHandleP指针为参数,以MSElementDescrP指针为返回值的转换函数实现从新的编程框架下的Element到COM框架下的Element的转换。不管是哪个方向的转换,我们都需要在Addins端有一个接收返回的指针并将其最终转换为Element对象实例。如果在C++/CLI中我们可以在一个代码块里边同时使用托管和非托管的对象,所以不管是哪个方向的转换我们只需要一个函数就能实现。

    在学习本章例子之前,需要安装好Mstn CE SDK。如何获取Mstn CE SDK请参考另一篇NativeCode的学习博客。接下来就让我们来一步一步实现这两个转换函数吧。

    1、在D盘建立:D:\Files\BentleyMstn\MstnCE\Mixed\SampleMixed目录。

    2、启动一个文本编辑器(当然可以启动VS2015用作编辑器),在其中键入如下内容并保存为文件D:\Files\BentleyMstn\MstnCE\Mixed\SampleMixed\SampleMixed.h。该文件中含有转换函数所在类的类型声明。以及一个继承与Bentley.DgnPlatformNET.Elements.Element的类ElementDerive。

    #pragma once
    
    #pragma managed
    
    #using <Bentley.MicroStation.dll>
    #using <Bentley.GeometryNET.Structs.dll>
    #using <Bentley.DgnPlatformNET.dll>
    #using <Bentley.Interop.MicroStationDGN.dll>
    
    #include <Mstn\MdlApi\MdlApi.h>
    #include <Mstn\ISessionMgr.h>
    #include <Mstn\MdlApi\mselems.h>
    #include <msclr/gcroot.h>
    #include <Mstn\MdlApi\dloadlib.h>
    
    USING_NAMESPACE_BENTLEY_DGNPLATFORM;
    USING_NAMESPACE_BENTLEY_MSTNPLATFORM;
    USING_NAMESPACE_BENTLEY_MSTNPLATFORM_ELEMENT;
    using namespace msclr;
    namespace GeoNet = Bentley::GeometryNET;
    namespace DgnNetEle = Bentley::DgnPlatformNET::Elements;
    namespace InteropMstn = Bentley::Interop::MicroStationDGN;
    
    namespace SampleMixed
    {
    		private ref class ElementDerive :public DgnNetEle::Element
    		{
    		public:
    			ElementDerive(ElementHandleP);
    		};
    
    		public ref class ElementOperation
    		{
    		public:
    			static DgnNetEle::Element^  ConvertToDgnNetEle(InteropMstn::Element^ ele);
    			
    			static InteropMstn::Element^ ElementOperation::ConvertToInteropEle(DgnNetEle::Element^ ele);
    		};
    }
    

    这里定义了一个ElementDerive类,是因为我们要借助于Bentley.DgnPlatformNET.Elements.Element的InitializeFromElementHandle函数来初始化构造Bentley.DgnPlatformNET.Elements.Element。因为InitializeFromElementHandle是保护类型的成员函数,不能从类的外边访问此成员函数,所以从Bentley.DgnPlatformNET.Elements.Element派生出ElementDerive类。在ElementDerive的成员函数中我们可以访问到基类的保护类型成员,所以可以通过InitializeFromElementHandle初始化基类Bentley.DgnPlatformNET.Elements.Element的成员。而ElementDerive是从Bentley.DgnPlatformNET.Elements.Element派生出来的,当然可以作为Bentley.DgnPlatformNET.Elements.Element来使用了。

    3. 在文本编辑器中再另建一个新文件,在其中键入如下内容并保存为文件D:\Files\BentleyMstn\MstnCE\Mixed\SampleMixed\SampleMixed.cpp。该文件中包含有ElementDerive和两个转换函数的实现。

    using namespace System::Reflection;
    
    #include <Mstn\MdlApi\mselemen.fdf>
    #include <vcclr.h>
    #include "SampleMixed.h"
    
    
    namespace SampleMixed
    {
    
    [assembly:AssemblyDelaySignAttribute(false)];
    
    	ElementDerive::ElementDerive(ElementHandleP eehp)
    	{
    		InitializeFromElementHandle(*eehp);
    	}
    
    	DgnNetEle::Element^ ElementOperation::ConvertToDgnNetEle(InteropMstn::Element^ ele)
    	{
    		ElementDerive^ rtnEle = nullptr;
    		//如果元素是从Dgn文件中读取出来的,且没有做任何修改,则其内部保存的ElementRef指针指向了Dgn文件在内存中的映射里的元素
    		if ((long)ele->MdlElementRef() != 0)
    		{
    			EditElementHandle eeh((ElementRefP)(void*)ele->MdlElementRef(), (DgnModelRefP)(void*)ele->ModelReference->MdlModelRefP());
    			rtnEle = gcnew ElementDerive(&eeh);
    			return rtnEle;
    		}
    		MSElementDescrP elmdscrP = (MSElementDescrP)(void*)ele->MdlElementDescrP(false);
    		EditElementHandle eeh(elmdscrP, true, false, (DgnModelRefP)(void*)ele->ModelReference->MdlModelRefP());
    		rtnEle = gcnew ElementDerive(&eeh);
    		return rtnEle;
    	}
    
    	InteropMstn::Element^ ElementOperation::ConvertToInteropEle(DgnNetEle::Element^ ele)
    	{
    		cli::pin_ptr<unsigned char> pb = &(ele->ElementHandle[0]);
    		ElementHandleP ehp = reinterpret_cast<ElementHandleP>(pb);
    		InteropMstn::Application^ msApp = Bentley::MstnPlatformNET::InteropServices::Utilities::ComApp;
    		MSElementDescrP elmdscrp = const_cast<MSElementDescrP>(ehp->GetElementDescrCP());
    		long long* lp = reinterpret_cast<long long*>(&*elmdscrp);
    		long long elmDscrp = *reinterpret_cast<long long*>(&lp);
    		InteropMstn::Element^ rtnEle = msApp->MdlCreateElementFromElementDescrP(elmDscrp);
    		return rtnEle;
    	}
    }

    ConvertToDgnNetEle实现从COM框架下的Element到新的编程框架下的Element的转换。这个函数通过COM框架下Element的MdlElementRef或者MdlElementDescrP获取到ElementRefP或者MSElementDescrP指针,通过这两个指针可以初始化构造非托管类型EditElementHandle。ElementDerive的构造函数需要一个ElementHandle的指针作为参数,而EditElementHandle派生于ElementHandle。所以我们通过EditElementHandle就可以初始化构造ElementDerive对象了,最后返回ElementDerive对象实例即可。ConvertToInteropEle实现从新的编程框架下的Element到COM框架下的Element的转换。函数中通过新的编程框架下的Element的ElementHandle属性获取到ElementHandle的指针。因为.Net中的对象实例在垃圾回收过程中会被重定位,所以我们使用C++/CLI中的类型cli::pin_ptr告诉编译器,在此cli::pin_ptr的作用域中ele->ElementHandle在垃圾回收时不被重新定位。这样就可以保证我们的ElementHandle指针一直有效。然后我们通过ElementHandle获取到元素的MSElementDesrP指针,进而去调用Bentley.Interop.MicroStationDGN.Application 下的MdlCreateElementFromElementDescrP成员函数的到COM框架下的Element实例。

    4. 在文本编辑器中再另建一个新文件,在其中键入如下内容并保存为文件D:\Files\BentleyMstn\MstnCE\Mixed\SampleMixed\SampleMixed.mke。该文件是SDK下的bmake工具用来编译源码来使用的,.mke文件的格式可以参考另一篇NativeCode的学习博客。

    DemoSrcDir   = $(_MakeFilePath)
    PolicyFile = MicroStationPolicy.mki
    appName    = SampleMixed
    ASSEMBLY_NAME       = $(appName)
    RIGHTSCOMPLIANT     = false
    
    MDLMKI = $(MSMDE)mki/
    
    mdlLibs = $(MSMDE)library/
    
    %include        mdl.mki
    
    outputDir = $(MS)Mdlapps/
    
    always:
        ~mkdir $(o)
    
    objList = $(o)SampleMixed$(oext)
    
    %include compileForCLRStart.mki
    
    CCompDebugOptions = $(CCompDebugOffSwitch)
    
    CCompDebugOptions =% $[CCompDebugDefault]
    
    CCompOpts + -AI$(MS)Assemblies -AI$(MS)Assemblies/ECFramework -AI$(MS) -AI$(o)
    
    dirToSearch = ${msPrivInc}
    
    $(o)SampleMixed$(oext) : $(baseDir)SampleMixed.cpp
    
    %include compileForCLRStop.mki
    
    DLM_OBJECT_DEST         = $(o)
    DLM_NAME                = $(appname)
    RIGHTSCOMPLIANT         = true
    DLM_DEST                = $(outputDir)
    DLM_OBJECT_FILES        = $(objList)
    DLM_NO_DEF              = 1
    DLM_NO_DLS              = 1
    DLM_NO_IMPLIB           = 1
    DLM_NO_SIGN = 1
    
    ASSEMBLY_VERSION        = 1.0.0.0
    ASSEMBLY_TITLE          = $(appName)
    ASSEMBLY_DESCRIPTION    = MixedMode Test Application
    ASSEMBLY_PRODUCT_NAME   = $(appName)
    ASSEMBLY_FILE_VERSION   = 1.0.0.0
    ASSEMBLY_COMPANY_NAME   = Bentley Systems
    ASSEMBLY_COPYRIGHT      = Copyright: (c) 2019 Bentley Systems, Incorporated. All rights reserved.
    
    LINKER_LIBRARIES            + $(mdlLibs)BentleyAllocator.lib
    LINKER_LIBRARIES            + $(mdlLibs)DgnPlatform.lib
    
    %include linkMixedAssembly.mki
    

    【注】:该mke文件的最后一行需要增加一个回车。由于论坛本身的原因,我们的回车被自动抹去了。请您粘贴完此mke文件代码后自己在最后一行后来按一个回车。

    4. 右键选择“开始>Bentley>MicroStation CONNECT Edition SDK”,选择“More>Run as administrator”启动bmake工具。在命令提示符后键入cd /d D:\Files\BentleyMstn\MstnCE\Mixed\SampleMixed并回车进入我们的源码所在目录,然后再键入bmake –a来生成SampleMixed.dll。生成的文件位于…\MicroStation\mdlapps目录下。

    SampleMixed.7z6136.SampleMixed_DLL.7z

    至此我们的两个转换函数已经封装好了,接下来我们在csAddins项目中去验证我们这两个函数。在csAddins项目中添加引用SmaleMixed.dll,具体添加方法参考第一章。打开commands.xml文件,新增命令两个命令csAddins CreateElement TestConvertToDgnNetEle和csAddins CreateElement TestConvertToInteropEle并指定其处理函数为csAddins.CreateElement. TestConvertToDgnNetEle和csAddins.CreateElement. TestConvertToInteropEle。如果您对XML格式的命令表文件还不熟悉,请参考第四章的相关主题。打开CreateElement.cs文件,CreateElement类的最后加入如下两个函数。

    public static void TestConvertToDgnNetEle(string unparsed)
            {
                BIM.Application app = Bentley.MstnPlatformNET.InteropServices.Utilities.ComApp;
                BIM.Point3d ptStart = app.Point3dZero();
                BIM.Point3d ptEnd = ptStart;
                ptStart.X = 10;
                BIM.LineElement lineEle = app.CreateLineElement2(null, ref ptStart, ref ptEnd);
                Element ele = SampleMixed.ElementOperation.ConvertToDgnNetEle(lineEle);
                ElementPropertiesSetter elePropSetter = new ElementPropertiesSetter();
                elePropSetter.SetColor(1);
                elePropSetter.SetWeight(2);
                elePropSetter.Apply(ele);
                ele.AddToModel();
            }
    
            public static void TestConvertToInteropEle(string unparsed)
            {
                DgnModel dgnModel = Session.Instance.GetActiveDgnModel();
                ModelInfo modelInfo = dgnModel.GetModelInfo();
                DPoint3d[] ptArr = new DPoint3d[5];
                ptArr[0] = new DPoint3d(0 * UorPerMas, 10 * UorPerMas, 0 * UorPerMas);
                ptArr[1] = new DPoint3d(1 * UorPerMas, 12 * UorPerMas, 0 * UorPerMas);
                ptArr[2] = new DPoint3d(3 * UorPerMas, 8 * UorPerMas, 0 * UorPerMas);
                ptArr[3] = new DPoint3d(5 * UorPerMas, 12 * UorPerMas, 0 * UorPerMas);
                ptArr[4] = new DPoint3d(6 * UorPerMas, 10 * UorPerMas, 0 * UorPerMas);
                CurvePrimitive curPri = CurvePrimitive.CreateLineString(ptArr);
                Element ele = DraftingElementSchema.ToElement(dgnModel, curPri, null);
                BIM.Element eleInterop = SampleMixed.ElementOperation.ConvertToInteropEle(ele);
                eleInterop.Color = 3;
                eleInterop.LineWeight = 4;
                ele.AddToModel();
            }
    

    选VS菜单Build > Build Solution来生成本解决方案。启动Mstn,打开key-in窗口,输入csAddins CreateElement TestConvertToDgnNetEle,可以看到在生成了如图所示的元素。

    元素我们是通过COM框架下的接口生成的,然后转换成新的编程框架下的元素,然后设置其颜色及线宽属性,最后添加到dgn文件中。接下来验证另一个函数,在key-in窗口,输入csAddins CreateElement TestConvertToInteropEle,可以看到在生成了如图所示的元素。

    元素我们是通过新的编程框架下的接口生成的,颜色及线宽我们是通过COM框架下的接口设置的。本章例子只是简单演示了C++/CLI的用途,实际上C++/CLI能为我们做很多东西。当然其难度也是Mstn平台主要的三种开发语言中最高的,要想掌握C++/CLI必须先对C#和C/C++中对象的生命周期管理以及内存回收机制有很深的了解才行。建议读者要先熟练使用C#和C/C++之后,再开始利用C++/CLI进行开发。完整的csAddins解决方案源文件下载链接如下。非常欢迎大家批评指正,非常欢迎与他人分享该博客。

    4721.csAddins.7z

    • Share
    • History
    • More
    • Cancel
    • HongQiang Guo Created by Bentley Colleague HongQiang Guo
    • When: Mon, Jan 28 2019 3:24 AM
    • HongQiang Guo Last revision by Bentley Colleague HongQiang Guo
    • When: Wed, Mar 3 2021 9:52 AM
    • Revisions: 6
    • Comments: 0
    Recommended
    Related
    Communities
    • Home
    • Getting Started
    • Community Central
    • Products
    • Support
    • Secure File Upload
    • Feedback
    Support and Services
    • Home
    • Product Support
    • Downloads
    • Subscription Services Portal
    Training and Learning
    • Home
    • About Bentley Institute
    • My Learning History
    • Reference Books
    Social Media
    •    LinkedIn
    •    Facebook
    •    Twitter
    •    YouTube
    •    RSS Feed
    •    Email

    © 2023 Bentley Systems, Incorporated  |  Contact Us  |  Privacy |  Terms of Use  |  Cookies