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社区-威客 第8章、在Addins中调用C/C++函数
    • 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. 

    第8章、在Addins中调用C/C++函数

    在第零章中我介绍过在MSTN CE版本上Addins开发方式新增加了一套与NativeCode架构几乎“平行”的编程框架。这里用了“几乎” 是因为C#和C/C++是两种完全不同的语言,虽然有很多相似的地方,但是要做到完全平行是不可能的。所以如果我们选择Addins开发方式的话,开发过程中会偶尔遇到NativeCode能实现的功能,在Addins的编程框架下找不到对应的接口。这个时候我们就要想办法自己动手在Addins中调用C/C++的接口了。有两种方式可以实现,一种是通过C#的PInvoke调用NativeCode端导出的函数,另一种是通过C++/CLI直接实现C#和C/C++的混合编程。本章简单介绍一下PInvoke,下一章介绍C++/CLI。

    Level是Mstn中对元素分组的一种方式。通过把元素放到不同Level下,可以对元素分组管理。Addins和NativeCode中都有获取当前Dgn文件中Level名字的接口。但是当你使用Addins中的接口获取Level名字的时候你会发现有些Level的名字获取不到。但是NativeCode下却能全部得到。原因是Dgn文件中的Level是由两部分组成的,一部分是存储在当前Dgn文件中的Level,另外一部分是从WorkSpace下的DgnLib中加载的Level。NativeCode下的接口这两部分Level的名字都可以获取到,而Addins下只能获取到当前Dgn文件中Level的名字。本章我们要在C#端通过PInvoke调用已经在NativeCode端封装好的一个函数来获取Dgn文件中所有Level的名字。

    本文主要介绍Addins开发,所以NativeCode端如何获取Level名字不再详细讲解,这里只把相关代码以及编译好的DLL上传上来。具体如何编译请参考另一篇NativeCode的学习博客。在NativeCode端我们导出了两个函数GetDgnlibLevelNames和ReleaseDgnlibLevelNames。GetDgnlibLevelNames获取Level的名字,ReleaseDgnlibLevelNames负责释放动态申请的内存。我们将在Addins中封装调用这两个函数。请将SampleNative.dll下载后拷贝到…\ Bentley\MicroStation CONNECT Edition\MicroStation\Mdlapps下。

    SampleNative.7zSampleNative_DLL.7z

    下面就让我们一步步来实现如何在C#的Addin中调用这两个函数。

    1. 打开commands.xml文件,新增命令csAddins DemoForm ShowLevelNames并指定其处理函数为csAddins.DemoForm.ShowLevelNames。如果您对XML格式的命令表文件还不熟悉,请参考第四章的相关主题。

    2. 选择VS菜单Project->csAddins Properties…,切换到Build下,选中“Allow Unsafe Code”复选框,如下图所示。

    3. 打开DemoForm.cs文件,在代码开头部分增加如下的using语句。

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

    4. 翻到该文件的尾部,增加命令处理函数ShowLevelNames以及调用外部函数所需要的DllImport属性声明,最终的源代码如下。

    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;
                //GetDgnlibLevelNames返回的是一个字符串指针数组,即数组中每一项都是一个字符串指针,参数中返回了数组的维度
                void** namesvpp = GetDgnlibLevelNames(ref namesCnt);
                IntPtr ptr = new IntPtr(namesvpp);
                //迭代数组中的每一个字符串,将字符串转换为托管字符串添加到list中
                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. 在VS中重新生成csAddins。然后启动Mstn并打开一个dgn文件,键入mdl load csAddins装载csAddins,再键入csaddins demoform showlevelnames,在Mstn的消息中心(双击Mstn底部的消息栏可以打开)就会显示出当前dgn文件中的所有Level的名字,包括从Dgnlib中加载的Level。如下所示是在我的机器上运行的结果。

    这里我们封装的两个函数的参数及返回值类型都是很简单的基本类型,实际开发过程中参数类型是很复杂的,例如参数类型是C++的类或者结构体。这样的函数封装的时候我们需要先在Addins端声明定义一个在二进制布局上与C++端的类或者结构体一致的类型,然后才能通过DllImport属性声明去调用NativeCode端的函数。具体如何在C#中声明定义与C++中的类或者结构体一致的类型不在本文的讨论范围,网上有很多相关的资料,读者可以自行到网上去搜索。

    如下链接为最终版本的csAddins源代码供您参考。

    2086.csAddins_chapter8.7z

    • Share
    • History
    • More
    • Cancel
    • HongQiang Guo Created by Bentley Colleague HongQiang Guo
    • When: Mon, Jan 28 2019 2:25 AM
    • HongQiang Guo Last revision by Bentley Colleague HongQiang Guo
    • When: Thu, Mar 4 2021 2:50 AM
    • Revisions: 5
    • 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