zoukankan      html  css  js  c++  java
  • MSCE C#官网一步步学习搬运9 第九章、用C++/CLI编写Addins

    第九章、用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:FilesBentleyMstnMstnCEMixedSampleMixed目录。

    2、启动一个文本编辑器(当然可以启动VS2015用作编辑器),在其中键入如下内容并保存为文件D:FilesBentleyMstnMstnCEMixedSampleMixedSampleMixed.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 <MstnMdlApiMdlApi.h>
    #include <MstnISessionMgr.h>
    #include <MstnMdlApimselems.h>
    #include <msclr/gcroot.h>
    #include <MstnMdlApidloadlib.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:FilesBentleyMstnMstnCEMixedSampleMixedSampleMixed.cpp。该文件中包含有ElementDerive和两个转换函数的实现。

    using namespace System::Reflection;
    
    #include <MstnMdlApimselemen.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;
    		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:FilesBentleyMstnMstnCEMixedSampleMixedSampleMixed.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
    

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

    至此我们的两个转换函数已经封装好了,接下来我们在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解决方案源文件下载链接如下。非常欢迎大家批评指正,非常欢迎与他人分享该博客。

  • 相关阅读:
    PHPStorm--美化
    Laravel5.1 模型--关联关系(复杂)
    HTML5 getUserMedia/AudioContext 打造音谱图形化
    如何在终端实时展现当前运行的git分支
    关于ionic的跨域问题
    ionic插件安装与卸载
    虚拟机调试ionic项目
    ionic双击退出键退出APP
    npm install 镜像
    对Date的扩展,将 Date 转化为指定格式的String
  • 原文地址:https://www.cnblogs.com/JolinZH/p/13125157.html
Copyright © 2011-2022 走看看