AutoCAD .NET开发大师Kean有价值的博客 2006年8月 .NET内容整理

一 Calling AutoCAD commands from .NET

使用.NET调用AutoCAD命令

In this earlier entry I showed some techniques for calling AutoCAD commands programmatically from ObjectARX and from VB(A). Thanks to Scott Underwood for proposing that I also mention calling commands `me via IM this evening with the C# P/Invoke declarations for ads_queueexpr() and acedPostCommand(). It felt like the planets were aligned... :-)

在早期的帖子中展示过以ObjectARX和VB编程的方式实现调用AutoCAD的内置命令,感谢Scott Underwood建议我今晚也提及一下利用PInvoke的方式调用ads_queueexpr和acedPostCommand

Here are some ways to send commands to AutoCAD from a .NET app:
四中方式

  • SendStringToExecute
    from the managed document object(.NET)
  • SendCommand
    from the COM document object(COM)
  • acedPostCommand
    via P/Invoke(ARX)
  • ads_queueexpr()
    via P/Invoke(ADS)

Here‘s some VB.NET code - you‘ll
need to add in a COM reference to AutoCAD 2007 Type Library in addition to the
standard .NET references to acmgd.dll and acdbmgd.dll.

Vb.net代码展示,需要引用COM库,.net基类

Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.Interop
Public Class SendCommandTest
    Private Declare Auto Function ads_queueexpr Lib "acad.exe" _
        (ByVal strExpr As String) As Integer
    Private Declare Auto Function acedPostCommand Lib "acad.exe" _
        Alias "[email protected]@[email protected]" _
        (ByVal strExpr As String) As Integer
    <CommandMethod("TEST1")> _
    Public Sub SendStringToExecuteTest()
        Dim doc As Autodesk.AutoCAD.ApplicationServices.Document
        doc = Application.DocumentManager.MdiActiveDocument
        doc.SendStringToExecute("_POINT 1,1,0 ", False, False, True)
    End Sub
    <CommandMethod("TEST2")> _
    Public Sub SendCommandTest()
        Dim app As AcadApplication = Application.AcadApplication
        app.ActiveDocument.SendCommand("_POINT 2,2,0 ")
    End Sub
    <CommandMethod("TEST3")> _
    Public Sub PostCommandTest()
        acedPostCommand("_POINT 3,3,0 ")
    End Sub
    <CommandMethod("TEST4")> _
    Public Sub QueueExprTest()
        ads_queueexpr("(command""_POINT"" ""4,4,0"")")
    End Sub
End Class
In case you‘re working in C#, here are the declarations of acedPostCommand() and ads_queueexpr() that Jorge sent to me:
[DllImport("acad.exe", CharSet = CharSet.Auto,
  CallingConvention = CallingConvention.Cdecl)]
extern static private int ads_queueexpr(string strExpr);
[DllImport("acad.exe", CharSet = CharSet.Auto,
  CallingConvention = CallingConvention.Cdecl,
  EntryPoint = "[email protected]@[email protected]")]
extern static private int acedPostCommand(string strExpr);

You‘ll need to specify:

using System.Runtime.InteropServices;

to get DllImport to work, of course.

二 Import blocks from an external DWG file using .NET

We‘re going to use a "side database" - a drawing that is loaded in memory, but not into the AutoCAD editor - to import the blocks from another drawing into the one active in the editor.

意思:不打开外部的dwg向图形空间导入实体

Here‘s some C# code. The inline comments describe what is being done along the way. Incidentally, the code could very easily be converted into a RealDWG application that works outside of AutoCAD (we would simply need to change the destDb from the MdiActiveDocument‘s Database to the HostApplicationServices‘ WorkingDatabase, and use a different user interface for getting/presenting strings from/to the user).

我们要使用一个辅助数据库“side database”――一个被载入内存,但不在AutoCAD编辑器的显示的图纸,使用这个辅助数据库来从其它的图纸中导入块到编辑器中的活动图纸。  下面是关于此的C#代码,其中的注释描述了每个步骤。顺便说一句,下面的代码很容易被转换成一个RealDWG程序,这个程序工作于AutoCAD外部(只需要简单地把目标数据库(destDb)从MdiActiveDocument的数据库改变成HostApplicationService的WorkingDatabase,并使用不同的用户接口来为用户得到和表现字符串)

using System;
using Autodesk.AutoCAD;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using System.Collections.Generic;

namespace BlockImport
{
  public class BlockImportClass
  {
    [CommandMethod("IB")]
    public void ImportBlocks()
    {
      DocumentCollection dm =
          Application.DocumentManager;
      Editor ed = dm.MdiActiveDocument.Editor;
      Database destDb = dm.MdiActiveDocument.Database;
      Database sourceDb = new Database(false, true);
      PromptResult sourceFileName;
      try
      {
        // Get name of DWG from which to copy blocks
sourceFileName =
ed.GetString("\nEnter the name of the source drawing: ");
        // Read the DWG into a side database
sourceDb.ReadDwgFile(sourceFileName.StringResult,
System.IO.FileShare.Read,
                            true,
                            "");

        // Create a variable to store the list of block identifiers
        ObjectIdCollection blockIds = new ObjectIdCollection();

Autodesk.AutoCAD.DatabaseServices.TransactionManager tm =
sourceDb.TransactionManager;

        using (Transaction myT = tm.StartTransaction())
        {
          // Open the block table
          BlockTable bt =
              (BlockTable)tm.GetObject(sourceDb.BlockTableId,
                                      OpenMode.ForRead,
                                      false);

          // Check each block in the block table
          foreach (ObjectId btrId in bt)
          {
            BlockTableRecord btr =
              (BlockTableRecord)tm.GetObject(btrId,
                                            OpenMode.ForRead,
                                            false);
            // Only add named & non-layout blocks to the copy list
            if (!btr.IsAnonymous && !btr.IsLayout)
blockIds.Add(btrId);
btr.Dispose();
          }
        }
        // Copy blocks from source to destination database
        IdMapping mapping = new IdMapping();
sourceDb.WblockCloneObjects(blockIds,
destDb.BlockTableId,
mapping,
                                    DuplicateRecordCloning.Replace,
                                    false);
ed.WriteMessage("\nCopied "
+ blockIds.Count.ToString()
                        + " block definitions from "
+ sourceFileName.StringResult
                        + " to the current drawing.");
      }
      catch(Autodesk.AutoCAD.Runtime.Exception ex)
      {
ed.WriteMessage("\nError during copy: " + ex.Message);
      }
sourceDb.Dispose();
    }
  }
}

And that‘s all there is to it. More information on the various objects/properties/methods used can be found in the ObjectARX Reference.

三 Breaking it down - a closer look at the C# code for importing blocks

I didn‘t spend as much time as would have liked talking about the code in the previous topic(it was getting late on Friday night when I posted it). Here is a breakdown of the important function calls.

The first major thing we do in the code is to declare and instantiate a new Database object. This is the object that will represent our in-memory drawing (our side database). The information in this drawing will be accessible to us, but not loaded in AutoCAD‘s editor.

Database sourceDb = new Database(false, true);

Very importantly, the first argument (buildDefaultDrawing) is false. You will only ever need to set this to true in two situations. If you happen to pass in true by mistake when not needed, the function will return without an error, but the DWG will almost certainly be corrupt. This comes up quite regularly, so you really need to watch for this subtle issue.

Here are the two cases where you will want to set buildDefaultDrawing to true:

  1. When you intend to create the drawing yourself, and not read it in from somewhere
  2. When you intend to read in a drawing that was created in R12 or before

Although this particular sample doesn‘t show the technique, if you expect to be reading pre-R13 DWGs into your application in a side database, you will need to check the DWG‘s version and then pass in the appropriate value into the Database constructor. Here you may very well ask, "but how do I check a DWG‘s version before I read it in?" Luckily, the first 6 bytes of any DWG indicate its version number (just load a DWG into Notepad and check out the initial characters):

AC1.50 = R2.05
AC1002 = R2.6
AC1004 = R9
AC1006 = R10
AC1009 = R11/R12
AC1012 = R13
AC1014 = R14
AC1015 = 2000/2000i/2002
AC1018 = 2004/2005/2006
AC1021 = 2007

You‘ll be able to use the file
access routines of your chosen programming environment to read the first 6
characters in - AC1009 or below will require a first argument of true,
otherwise you‘re fine with false.

Next we ask the user for the path
& filename of the DWG file:

sourceFileName =
  ed.GetString("\nEnter the name of the source drawing: ");

Nothing very interesting here, other than the fact I‘ve chosen not to check whether the file actually exists (or even whether the user entered anything). The reason is simple enough: the next function call (to ReadDwgFile()) will throw an exception if the file doesn‘t exist, and the try-catch block will pick this up and report it to the user. We could, for example, check for that particular failure and print a more elegant message than "Error during copy: eFileNotFound", but frankly that‘s just cosmetic - the exception is caught and handled well enough.

sourceDb.ReadDwgFile(sourceFileName.StringResult,
                     System.IO.FileShare.Read,
                     true,
                     "");

This is the function call that reads in our drawing into the side database. We pass in the results of the GetString() call into the filename argument, specifying we‘re just reading the file (for the purposes of file-locking: this simply means that other applications will be able to read the DWG at the same time as ours but not write to it). We then specify that we wish AutoCAD to attempt silent conversion of DWGs using a code-page (a pre-Unicode concept related to localized text) that is different to the one used by the OS we‘re running on. The last argument specifies a blank password (we‘re assuming the drawing being opened is either not password protected or its password has already been entered into the session‘s password cache).

Next we instantiate a collection object to store the IDs of all the blocks we wish to copy across from the side database to the active one:

ObjectIdCollection blockIds = new ObjectIdCollection();

We then create a transaction which will allow us to access interesting parts of the DWG (this is the recommended way to access DWG content in .NET). Using the transaction we open the block table of the side database for read access, specifying that we only wish to access it if it has not been erased:

BlockTable bt =
    (BlockTable)tm.GetObject(sourceDb.BlockTableId,
                             OpenMode.ForRead,
                             false);

From here - and this is one of the beauties of using the managed API to AutoCAD - we simply use a standard foreach loop to check each of the block definitions (or "block table records" in AcDb parlance).

foreach (ObjectId btrId in bt)
{
  BlockTableRecord btr =
    (BlockTableRecord)tm.GetObject(btrId,
                                   OpenMode.ForRead,
                                   false);
  // Only add named & non-layout blocks to the copy list
  if (!btr.IsAnonymous && !btr.IsLayout)
    blockIds.Add(btrId);
  btr.Dispose();
}

This code simply opens each block definition and only adds its ID to the list to copy if it is neither anonymous nor a layout (modelspace and each of the paperspaces are stored in DWGs as block definitions - we do not want to copy them across). We also call Dispose() on each block definition once we‘re done (this is a very good habit to get into).

And finally, here‘s the function call that does the real work:

sourceDb.WblockCloneObjects(blockIds,
                            destDb.BlockTableId,
                            mapping,
                            DuplicateRecordCloning.Replace,
                            false);

  

WblockCloneObjects() takes a list of objects and attempts to clone them across databases - we specify the owner to be the block table of the target database, and that should any of the blocks we‘re copying (i.e. their names) already exist in the target database, then they should be overwritten ("Replace"). You could also specify that the copy should not happen for these pre-existing blocks ("Ignore").

四 Supporting multiple AutoCAD versions - conditional compilation in C#

简要介绍一下吧,就不翻译啦,意思就是在命令注册前添加一段[Conditional("AC2007")]这个特性,就可以实现在版本升级的时候,屏蔽函数的变化,减少代码重构。

Someone asked by email how to get the block import code first shown last week and further discussed in yesterday‘s post working with AutoCAD 2006. I‘ve been using AutoCAD 2007 for this, but to get it working there are only a few changes to be made.

Firstly you‘ll need to make sure you have the correct versions of acmgd.dll and acdbmgd.dll referenced into the project (you should be able to tell from their path which version of AutoCAD they were installed with).

Secondly you‘ll need to modify your code slightly, as WblockCloneObjects() has changed its signature from 2006 to 2007. The below code segment shows how to use pre-processor directives to implement conditional compilation in C#. You will need to set either AC2006 or AC2007 as a "conditional compilation symbol" in your project settings for this to work:

#if AC2006
        mapping = sourceDb.WblockCloneObjects(blockIds,
                                              destDb.BlockTableId,
                                              DuplicateRecordCloning.Replace,
                                              false);
#elif AC2007
        sourceDb.WblockCloneObjects(blockIds,
                                    destDb.BlockTableId,
                                    mapping,
                                    DuplicateRecordCloning.Replace,
                                    false);
#endif

You would possibly use #else rather than #elif above, simply because that would make your code more future-proof: it would automatically support new versions without needing to specifically add code to check for them.

On a side note, you can actually specify that entire methods should only be compiled into a certain version. Firstly, you‘ll need to import the System.Diagnostics namespace:

using System.Diagnostics;

Then you simply use the Conditional attribute to declare a particular method (in this case a command) for a specific version of AutoCAD (you might also specify a debug-only command by being conditional on the DEBUG pre-processor symbol, should you wish):

namespace BlockImport
{
  public class BlockImportClass
  {
    [Conditional("AC2007"),CommandMethod("IB")]
    public void ImportBlock()
    {
...

  

时间: 2024-12-12 08:23:16

AutoCAD .NET开发大师Kean有价值的博客 2006年8月 .NET内容整理的相关文章

OS/mac开发的一些知名个人博客

OS/mac开发的一些知名个人博客 王巍的博客:王巍目前在日本横滨任职于LINE.工作内容主要进行Unity3D开发,8小时之外经常进行iOS/Mac开发.他的陈列柜中已有多款应用,其中番茄工作法工具非常棒.http://onevcat.com池 建强的博客: 池建强,70后程序员,Blogger.98年毕业,先后就职于洪恩软件.RocketSofeware和用友软件工程公司(后更名为瑞友 科技),现任瑞友科技IT应用研究院副院长.该博客最初每天发送一条Mac技巧,不过目前已经形成了一种技术和人

iOS/mac开发的一些知名个人博客

王巍的博客:王巍目前在日本横滨任职于LINE.工作内容主要进行Unity3D开发,8小时之外经常进行iOS/Mac开发.他的陈列柜中已有多款应用,其中番茄工作法工具非常棒.http://onevcat.com池 建强的博客: 池建强,70后程序员,Blogger.98年毕业,先后就职于洪恩软件.RocketSofeware和用友软件工程公司(后更名为瑞友 科技),现任瑞友科技IT应用研究院副院长.该博客最初每天发送一条Mac技巧,不过目前已经形成了一种技术和人文结合的风格,时而随笔,时而技术.h

Android应用开发-小巫CSDN博客客户端之显示博文详细内容

Android应用开发-小巫CSDN博客客户端之显示博文详细内容 上篇博文给大家介绍的是如何嵌入有米广告并且获取收益,本篇博客打算讲讲关于如何在一个ListView里显示博文的详细信息,这个可能是童鞋们比较困惑的,因为一篇博客可能有标题.摘要.图片.代码等等元素组成,我们要怎么在一个界面中显示这些内容并且按照自己的指定的方式显示呢,别急,下面会告诉大家. 重新整理一下一篇博文可能有以下元素: 标题 摘要 文本内容 图片 粗标题 代码块 在UI篇小巫已经介绍了,博文详细内容的主要控件就是一个Lis

即日起,博客将不定时更新技术内容

Hello,everybody!这是我新开的博客,以后我会将我收藏的资源以及修改之后的资源发布到这里,希望对大家有所帮助. 同时,本人的个人贴吧也开通了,欢迎有志之士加入我这个大家庭,帮助更多开发者,共同学习,共同进步. 贴吧链接 吕昌辉吧 即日起,博客将不定时更新技术内容,布布扣,bubuko.com

《flask web开发》第11章 博客文章发布表单无法显示的解决方案

有不少同学在这一章都发现自己明明按照书上一步步执行,但却在首页看不到博客文章发布表单.这个问题其实很好解决. 首先,下载一个DB Browser for SQLite.Ubuntu用户可以在终端输入以下命令: sudo apt-get install sqlitebrowser 之后用这个数据库浏览器打开flasky里的数据库,在role表中按书中第9章所述,将所有角色手工录入,并在user表中给自己的账户添加上role_id字段,这样就大功告成了! 参考: https://www.zhihu.

三天独立开发的iOS端CSDN博客阅读器上线了

作为CSDN博客的忠实读者,作为一个iOS开发者,实在是不能忍受手机上无法看CSDN博客的不便,五一节抽空做了一个简单的CSDN博客阅读器,第一版仅提供博客首页热门文章查看功能和阅读个人博客功能.如果你认为有其他值得实现的优秀功能或者发现了bug,欢迎及时联系我. 由于第一天上架,AppStore还无法搜索到.从5.17日起,直接搜索csdn博客即可. 下载地址:https://itunes.apple.com/us/app/csdn-bo-ke/id991337359?l=zh&ls=1&

学习过程中遇到的比较有价值的博客--整理

转发请注明出处,本博客分享的内容本着对学习,感谢原博客的分享. Junit 入门使用教程 https://www.cnblogs.com/ysocean/p/6889906.html Java 反编译工具下载 https://www.cnblogs.com/ysocean/p/6828315.html Java 集合详解 https://www.cnblogs.com/ysocean/p/6555373.html Ajax 的用法 https://www.cnblogs.com/ysocean/

291.博客园安卓&amp;苹果手机客户端开源项目整理

1.简介 1.1本贴简介 本贴主要收集一些博客园安卓&苹果端的开源项目,方便园友参与开发和下载,各个风格不同,根据各人审美参与开发和下载使用,欢迎下方留言投稿. 1.2本贴说明 本贴为个人整理,与博客园官方无关,其中所有观点看法均为个人看法,不代表博客园官方和开发者,如有侵权或言论不良,请下方留言,必定删除或修改. 下方排序为本文博主接触及收集顺序,并不是安全性.美观性等标准的排序. 博主简评为个人看法,并不代表该应用的全部特点,只是选取个人认为比较方便实用或者突出的特点进行说明,如有本同看法欢

CSDN博客2014年4月24日清理缓存

亲爱的CSDN博主们.我们将于今天(2014年4月24日)对CSDN博客频道缓存进行清理,假设您登录后发现自己的文章总数.积分.评论数.訪问数出现异常,请不要慌张.您的数据并没有丢失.将会在缓存清理完毕后(估计在今晚21:00前完毕)所有恢复. 如到时您依然发现博客异常,请及时联系: 客服微博:@CSDN产品客服 客服QQ:2355263776 我们将在第一时间为您解决.再次感谢您的支持和理解.