版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、外部 .net程序与 autocad 交互1 外部.net 程序与 autocad交互(autocad)开发者一般要么将功能集成到autocad( 通过其扩展体系来添加命令、用户界面元素、对象等 ) ,要么通过程序来驱动autocad ,使通用的任务自动化。这两种方式的界线有时候不是那么明显,我今天要关注的是后者。为了后面的解释方便,我先介绍一下两种应用程序的交互。一、线程外此种情况下,我们需要在两个独立的可执行程序之间进行通信。就好比如我需要一个 .exe的执行程序来驱动autocad ,我们就需要找到某种方式来启动autocad并且与之通信最典型的就是使用com 技术或更早的 dde 技术
2、。这种通信方式,确切地说,是通过进程间通信ipc(inter-process communication) 来完成,这种方式在传输大量数据时是非常低效的。 这就是早期的 ads 和外部 vb应用程序运行很慢的原因。二、线程内当程序代码被编译成dll ,不管是 vb建立的 activex dll,objectarx模块,还是.net的程序集, 程序与 autocad 主线程之间的通信都要高效得多数据结构可以通过指针来传递或直接引用,而不是通过低效的ipc编组来发送数据信息。目前 autocad大部分的 apis 都是被设计用于 线程内 的包括 lisp,objectarx,和.net api。因
3、为 .net remoting 的实用性,很多人常希望或期盼着autocad能通过 .net从线程外部驱动, 不过.net的托管 api 并不是这么设计的它其实就是对 objectarx的一个封装,其运行是以通过指针对内部对象的直接访问为基础的,完全不可能超越线程的界线。com 自动化技术的最大特点之一就是它就是被设计成既可以用于线程外( 通过外部 exe)也可以用于线程内 ( 通过 vba或通过getinterfaceobject()来调用 vb6的 activex dll)。目前这仍然是从外部可执行程序驱动 autocad的最好方式。通常我不建议在进程间传递太多的信息。如果你需要从外部程序
4、驱动autocad ,最好只是通过它运行autocad(或是在可能的情况下连接到一个已运行的实例) ,接下来加载一个线程内的模块,让它在autocad的进程内完成主要的任务。外部 .net程序与 autocad 交互2 下面的代码就将展示如何用c来完成这一过程。它会尝试连接到一个已运行的 autocad实例(这是随意的你也可以把代码修改成直接运行一个autocad),如果失败则运行。一旦有了正在运行的对象实例,使之可见且运行一个自定义命令。建议将程序设置成自动加载要么在autocad启动时加载,要么在命令被触发时加载,然后运行一个模块中定义的命令。你需要添加对“ autocad type li
5、brary ”的引用,还有导入以下命名空间。using system; using system.runtime.interopservices; using autodesk.autocad.interop; 下面的代码你可以添加到比如某个按钮的click事件中或其他有效的函数中去。 代码如下 / autocad.application.17 uses 2007 or 2008, / whichever was most recently run / autocad.application.17.1 uses 2008, specifically conststring progid = a
6、utocad.application.17.1; acadapplication acapp = null ; try acapp = (acadapplication)marshal.getactiveobject(progid); catch try type actype = type.gettypefromprogid(progid); acapp = (acadapplication)activator.createinstance( actype, true ); catch messagebox.show( cannot create object of type + progi
7、d + ); 外部 .net程序与 autocad 交互3 if (acapp != null ) / by the time this is reached autocad is fully / functional and can be interacted with through code acapp.visible = true ; acapp.activedocument.sendcommand(_mycommand ); 前面讨论了如何通过com 接口从 .net程序运行 autocad并执行命令。 这样就可以通过托管 api 与 autocad在其进程范围内交互。的确,这种方法
8、在不需要返回结果时还是不错的,但是如果需要返回结果,那就有局限了。你可以通过autocad用户变量或创建文件供程序来读取来达到返回结果的目的,不过这些方法都比较繁琐。所以,我决定尝试,尽管我一开始对此持怀疑态度。下面是我用来实现这一目的的步骤:首先, 按照通常引用 acmgd.dll,acdbmgd.dll的方法来创建一个类库作为进程内的部分,添加的c#代码如下: 代码如下 using autodesk.autocad.applicationservices; using autodesk.autocad.editorinput; using autodesk.autocad.database
9、services; using autodesk.autocad.runtime; using autodesk.autocad.geometry; using system.runtime.interopservices; namespace loadablecomponent progid(loadablecomponent.commands ) publicclass commands / 一个简单的命令/ 用来测试程序是否被正确加载 commandmethod ( mycommand) publicvoid mycommand() document doc = application.
10、documentmanager.mdiactivedocument; editor ed = doc.editor ; ed.writemessage(ntest command executed.); 外部 .net程序与 autocad 交互4 / 定义函数,将两数相加,并以结果为直径画圆/ 结果以 string返回,以测试不同类型的返回值publicstring addnumbers( int arg1,double arg2) / 测试时发现documentmanager.mdiactivedocument为空/ 无法供我们使用/ 所以改用 hostapplicationservice
11、s workingdatabase database db = hostapplicationservices.workingdatabase; document doc = application.documentmanager.getdocument(db); / 执行两数的和运算double res = arg1 + arg2; / 锁定文档 documentlock loc = doc.lockdocument(); using (loc) transaction tr = db.transactionmanager.starttransaction(); using (tr) / 创
12、建圆circle cir = newcircle ( newpoint3d (0, 0, 0), newvector3d (0, 0, 1), res ); cir.setdatabasedefaults(db); / 添加圆到目前的空间blocktablerecord btr = (blocktablerecord)tr.getobject( db.currentspaceid, openmode .forwrite ); btr.appendentity(cir); tr.addnewlycreateddbobject(cir, true ); / commit tr.commit();
13、/ 返回字符串结果return res.tostring(); 外部 .net程序与 autocad 交互5 你会发现我为 commands 类标记了一个progid-loadablecomponent.commands( 这并不必遵照 namespace.class 的规则,你也可以用其他的名称 ) 。确定将 assemblyinfo.cs文件中的 comvisible 编译属性改为 true(默认值为 false) ,否则不会有任何类在com 接口中暴露。以上的代码基本都相当简单,它仅包含一个命令用来确保在程序集加载时命令都被注册了。 addnumbers()函数用了一个略微不一样的方法来
14、获得文档对象和数据库,因为我发现 mdiactivedocument 在这时候会是 null 。我怀疑是时效的问题,如果 autocad有足够的时间来完成初始化,代码就不必这样写了。也许能有什么简单的方法来等待初始化的完成。编译好的程序需要通过com 注册,我趋向于通过 visual studio命令提示行 来完成。先将目录更改到程序集所在目录,然后运行regasm loadablecomponent.dll命令( 你也可以通过 /reg 选项来创建一个 .reg 文件而不是直接修改注册表 )。现在,我们能以 com 方式引用 autocad type library,autocad/obje
15、ctdbx common type library库来创建应用程序。 当然,还需要引用刚创建的 .net程序集。在默认窗体上添加一个按钮,并添加之前提及的代码,适当的修改下逻辑,让程序加载我们创建的组件并且执行addnumbers()函数。 代码如下 using autodesk.autocad.interop; using system.windows.forms; using system.runtime.interopservices; using system.reflection; using system; using loadablecomponent; namespace dr
16、ivingautocad public partial class form1 : form public form1() initializecomponent(); privatevoid button1_click(object sender, eventargs e) 外部 .net程序与 autocad 交互6 conststring progid = autocad.application.18; acadapplication acapp = null ; try acapp = (acadapplication)marshal.getactiveobject(progid);
17、catch try type actype = type.gettypefromprogid(progid); acapp = (acadapplication)activator.createinstance( actype, true ); catch messagebox.show( cannot create object of type + progid + ); if (acapp != null ) try / by the time this is reached autocad is fully / functional and can be interacted with
18、through code acapp.visible = true ; object app = acapp.getinterfaceobject(loadablecomponent.commands ); if (app != null ) / lets generate the arguments to pass in: / an integer and a double object args = 5, 6.3 ; / now lets call our method dynamically 外部 .net程序与 autocad 交互7 object res = app.gettype(
19、).invokemember( addnumbers, bindingflags.invokemethod, null , app, args ); acapp.zoomall(); messagebox.show( this , addnumbers returned: + res.tostring() ); catch (exception ex) messagebox.show( this , problem executing component: + ex.message ); 我试着用 application.getinterfaceobject()的方法来加载这是通过vba或 v
20、lisp 加载 vb6 activex dll的经典方法看看这一方法是否对于指定了progid的.net 程序是否也有效。貌似不起作用,不过模块中包含的命令都成功的注册了。一个不错的 surprise !一开始,我本想在类库中定义了执行程序调用的接口,但是到最后却用了一个更灵活的方法: 通过 getinterfaceobject()返回对象的 invokemember() 方法。这样不必去定义接口并暴露, 但却增加了操作的一点点不确定性( 我最近几周一直在说的:当我们得到灵活性时, 也会失去我们早已依赖的编译器的帮助) 。如果命令函数被声明成静态,也会碰到问题,不过我推测给invokememb
21、er() 正确的参数可能会解决问题。当我们运行程序并点击按钮时,就会看到一个直径为11.3 的圆被创建, 两个数的和的结果也被返回。外部 .net程序与 autocad 交互8 第一段就不翻了, 反正就是有人指出代码有问题了。最主要的问题就是 - 事实上程序并不是在 autocad的主线程内执行的 , 这样, 在与 autocad进行交互的过程中效率就被限制了。为了解决这一问题,我们可以通过system.enterpriseservices.servicedcomponent(需要添加对system.enterpriseservices的引用 ) 来驱动我们的程序。下面是loadablecom
22、ponent 更新后的 c#代码。 代码如下 using autodesk.autocad.applicationservices; using autodesk.autocad.editorinput; using autodesk.autocad.databaseservices; using autodesk.autocad.runtime; using autodesk.autocad.geometry; using system.runtime.interopservices; using system.enterpriseservices; namespace loadableco
23、mponent guid(5b5b731c-b37a-4aa2-8e50-42192bd51b17 ) publicinterface inumberaddition 外部 .net程序与 autocad 交互9 dispid(1) string addnumbers( int arg1, double arg2); progid(loadablecomponent.commands ), guid(44d8782b-3f60-4cae-b14d-fa060e8a4d01 ), classinterface(classinterfacetype.none) publicclass comman
24、ds : servicedcomponent, inumberaddition / a simple test command, just to see that commands / are loaded properly from the assembly commandmethod ( mycommand) staticpublicvoid mycommand() document doc = application.documentmanager.mdiactivedocument; editor ed = doc.editor ; ed.writemessage(ntest comm
25、and executed.); / a function to add two numbers and create a / circle of that radius. it returns a string / withthe result of the addition, just to use / a different return type publicstring addnumbers( int arg1, double arg2) / during tests it proved unreliable to rely / on documentmanager.mdiactive
26、document / (which was null) so we will go from the / hostapplicationservices workingdatabase document doc = application.documentmanager.mdiactivedocument; database db = doc.database ; editor ed = doc.editor ; ed.writemessage( nadd numbers called with 0 and 1., arg1, arg2 ); / perform our addition do
27、uble res = arg1 + arg2; / lock the document before we access it documentlock loc = doc.lockdocument(); using (loc) transaction tr = db.transactionmanager.starttransaction(); 外部 .net程序与 autocad 交互10 using (tr) / create our circle circle cir = newcircle ( newpoint3d (0, 0, 0), newvector3d (0, 0, 1), r
28、es ); cir.setdatabasedefaults(db); / add it to the current space blocktablerecord btr = (blocktablerecord)tr.getobject( db.currentspaceid, openmode .forwrite ); btr.appendentity(cir); tr.addnewlycreateddbobject(cir, true ); / commit the transaction tr.commit(); / return our string result return res.
29、tostring(); 有几点需要指出的是:我们现在用一个接口来暴露组件中的功能,这样使得我们返回数据的方式更加灵活;我们用 guidgen.exe 生成的特定的 guids来标记接口和组件, 虽然或许我们可以省略这步;现在我们可以安全地使用mdiactivedocument 属性, 还有通过 editor 输出消息;当生成组件并且通过regasm.exe 来注册时,可以得到如下的注册内容(内容略 ) 外部调用程序更新后的代码如下所示: 代码如下 using autodesk.autocad.interop; using system.windows.forms; using system.runtime.interopservices; using system.reflection; using system; 外部 .net程序与 autocad 交互11 using loadableco
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026江苏七维面试题及答案
- (2026年)CT-FFR指导冠心病患者的临床治疗
- 人工智能在金融普惠中的技术瓶颈-第6篇
- 辽宁省辽阳市辽阳县2025-2026学年七年级下学期期末质量监测地理试卷(含答案)
- 2026年广西壮族自治区百色市住房和城乡建设局人员招聘笔试参考题库及答案详解
- 2026浙江省医疗服务管理评价中心招聘编外人员1人考试备考题库及答案详解
- 2026年西安市曲江第二学校中小学教师招聘考试模拟试题及答案详解
- 2026年湖北省咸宁市网格员招聘笔试备考试题及答案详解
- 2026西安高新区第二十三小学(西安高新区西电附属小学)教师招聘考试备考题库及答案详解
- 2026浙江湖州市公路水运工程监理咨询有限公司招聘10人考试参考题库及答案详解
- 2027届高考数学一轮复习1.4基本不等式(课件)
- 2026年成都第八中学初一入学语文分班考试真题含答案
- 2026江苏苏州市相城区人力资源和社会保障局招聘编外人员3人笔试备考试题及答案详解
- 2026安徽九华山旅游发展股份有限公司招聘82人考试备考试题及答案详解
- 2026年河北省中考英语试题(含答案和音频)
- 手术室人文关怀实践分享
- 2026浙江舟山市定海区城东街道办事处第二批招聘城市管理辅助人员3人考试备考试题及答案详解
- 施工应急资源调配方案
- (期末复习) 2025-2026学年下学期人教版八年级下册数学期末 练习试卷
- 小儿心肌炎诊疗指南(2025年版)
- 2026年秋新教材人教版九年级上册英语Unit 1-8单词背记表
评论
0/150
提交评论