雪山飞狐_lzh 发表于 2009-12-12 20:55:00

为.net程序集实现Com接口

本帖最后由 作者 于 2009-12-12 22:33:10 编辑

下面是一个简单的示例,
详细见
http://bbs.mjtd.com/forum.php?mod=viewthread&tid=75796
7楼
一、
新建一个类库项目:TlsCad.Curve
在AssemblyInfo.cs文件中修改:

二、
添加引用:acdbmgd.dll,acmgd.dll
在Class1.cs中加入下列代码using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using AcDb = Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
using System.Runtime.InteropServices;
using System;
using System.Collections.Generic;
namespace TlsCad
{
   
    public class Curve
    {
      AcDb.ObjectId _id;
      public void Set(object acadObj)
      {
            _id = AcDb.Entity.FromAcadObject(acadObj);
      }
      private AcDb.Curve acCurve
      {
            get
            {
                AcDb.Database db = _id.Database;
                using (AcDb.Transaction tr = db.TransactionManager.StartTransaction())
                {
                  return tr.GetObject(_id, AcDb.OpenMode.ForRead) as AcDb.Curve;
                }
            }
      }
      public double StartParam
      {
            get
            {
                return acCurve.StartParam;
            }
      }
      public double EndParam
      {
            get
            {
                return acCurve.EndParam;
            }
      }
      public object GetClosestPointTo(object pnt, bool extend)
      {
            return acCurve.GetClosestPointTo(new Point3d((double[])pnt), extend).ToArray();
      }
      public double GetDistAtParam(double param)
      {
            return acCurve.GetDistanceAtParameter(param);
      }
      public double GetParamAtDist(double dist)
      {
            return acCurve.GetParameterAtDistance(dist);
      }
      public double GetParamAtPoint(object pnt)
      {
            return acCurve.GetParameterAtPoint(new Point3d((double[])pnt));
      }
      public double GetDistAtPoint(object pnt)
      {
            return acCurve.GetDistAtPoint(new Point3d((double[])pnt));
      }
      public object GetParamsByPoints(object pnts)
      {
            AcDb.Curve c = acCurve;
            List<double> pars = new List<double>();
            foreach (object pnt in (object[])pnts)
            {
                pars.Add(c.GetParameterAtPoint(new Point3d((double[])pnt)));
            }
            return pars.ToArray();
      }
      public object SplitByParams(object pars)
      {
            AcDb.Database db = _id.Database;
            List<object> objs = new List<object>();
            using (Application.DocumentManager.MdiActiveDocument.LockDocument())
            {
                using (AcDb.Transaction tr = db.TransactionManager.StartTransaction())
                {
                  AcDb.Curve c = tr.GetObject(_id, AcDb.OpenMode.ForRead) as AcDb.Curve;
                  AcDb.BlockTableRecord btr = tr.GetObject(c.OwnerId, AcDb.OpenMode.ForWrite) as AcDb.BlockTableRecord;
                  double[] parrs = (double[])pars;
                  Array.Sort(parrs);
                  foreach (AcDb.Curve cc in c.GetSplitCurves(new DoubleCollection(parrs)))
                  {
                        btr.AppendEntity(cc);
                        tr.AddNewlyCreatedDBObject(cc, true);
                        objs.Add(cc.AcadObject);
                  }
                  tr.Commit();
                }
            }
            return objs.ToArray();
      }
    }
}生成解决方案,将TlsCad.Curve.dll复制到AutoCad目录下
三、

切换到AutoCad目录下,在命令行键入
regasm TlsCad.Curve.dll /regfile /codebase
运行生成的reg文件,为.Net类库注册Com
四、
在Cad中绘制一条曲线,然后打开VB编辑器,加入下列代码调用Sub tt()
    Dim tCuve As Object
    Dim obj As Object, pnts(0)
    Set tCuve = Application.GetInterfaceObject("TlsCad.Curve")
    ThisDrawing.Utility.GetEntity obj, pnts(0)
    tCuve.Set obj
    pnts(0) = tCuve.GetClosestPointTo(pnts(0), False)
    tCuve.SplitByParams tCuve.GetParamsByPoints(pnts)
End Sub

gsteven 发表于 2009-12-12 22:02:00

<p>老大,我照你的方法做了一遍,但是注册的时候的消息不知道是不是注册成功了,在添加com引用的时候找不到TlsCad.Curve。</p><p>在执行VBA代码的时候,Set tCuve = Application.GetInterfaceObject("TlsCad.Curve") 这一句出错:运行时错误</p><p>'-2147221005(800401f3)' 加载应用程序时出现问题。</p><p></p>

雪山飞狐_lzh 发表于 2009-12-12 22:28:00

<p>添加com引用的时候确实找不到TlsCad.Curve</p><p>你运行生成的reg文件么?</p><p>把下面的rar解压到d:/tlscad/bin目录下试试</p><p>上面的VBA代码改动了一下</p>

gsteven 发表于 2009-12-13 15:20:00

老大,我按照你说的帖子的7楼的方法实现了com和ObjectAPI的调用,那8楼的方法和7楼却完全不一样,是不是7楼的方法有时候会碰到问题?

飞诗(fsxm) 发表于 2009-12-13 19:31:00

本人轻易不灌水,灌水只为顶狐哥!~

雪山飞狐_lzh 发表于 2009-12-13 21:31:00

<p>奇怪的问题是:上面的方法在VB或C#却不能使用,只能VBA?</p><p>有时间按8楼的代码试下</p>

雪山飞狐_lzh 发表于 2009-12-13 23:39:00

本帖最后由 作者 于 2009-12-14 0:06:50 编辑

代码更改了一下,这样可以了
结论是:
1、不要在NetApi中使用DBObject.FromAcadObject函数
2、注意当前文档加锁的方法
3、C#做Com反射真的好累!
C#代码:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using AcDb = Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
using System.Runtime.InteropServices;
using System;
using System.Collections.Generic;
namespace TlsCad.Common
{
   
    public class Curve
    {
      AcDb.ObjectId _id;
      public void Set(int id)
      {
            _id = new Autodesk.AutoCAD.DatabaseServices.ObjectId(new IntPtr(id));
      }
      private AcDb.Curve acCurve
      {
            get
            {
                AcDb.Database db = _id.Database;
                using (AcDb.Transaction tr = db.TransactionManager.StartTransaction())
                {
                  return tr.GetObject(_id, AcDb.OpenMode.ForRead) as AcDb.Curve;
                }
            }
      }
      public double StartParam
      {
            get
            {
                return acCurve.StartParam;
            }
      }
      public double EndParam
      {
            get
            {
                return acCurve.EndParam;
            }
      }
      public object GetClosestPointTo(object pnt, bool extend)
      {
            return acCurve.GetClosestPointTo(new Point3d((double[])pnt), extend).ToArray();
      }
      public double GetDistAtParam(double param)
      {
            return acCurve.GetDistanceAtParameter(param);
      }
      public double GetParamAtDist(double dist)
      {
            return acCurve.GetParameterAtDistance(dist);
      }
      public double GetParamAtPoint(object pnt)
      {
            return acCurve.GetParameterAtPoint(new Point3d((double[])pnt));
      }
      public double GetDistAtPoint(object pnt)
      {
            return acCurve.GetDistAtPoint(new Point3d((double[])pnt));
      }
      private object GetParamsByPoints(object pnts)
      {
            List<double> pars = new List<double>();
            AcDb.Database db = _id.Database;
            using (AcDb.Transaction tr = db.TransactionManager.StartTransaction())
            {
                AcDb.Curve c = tr.GetObject(_id, AcDb.OpenMode.ForRead) as AcDb.Curve;
                foreach (object pnt in (object[])pnts)
                {
                  pars.Add(c.GetParameterAtPoint(new Point3d((double[])pnt)));
                }
            }
            return pars.ToArray();
      }
      public void SplitByPoints(object pnts)
      {
            SplitByParams(GetParamsByPoints(pnts));
      }
      public object SplitByParams(object pars)
      {
            AcDb.Database db = _id.Database;
            List<object> objs = new List<object>();
            Document doc = Application.DocumentManager.GetDocument(AcDb.HostApplicationServices.WorkingDatabase);
            using (doc.LockDocument())
            {
                using (AcDb.Transaction tr = db.TransactionManager.StartTransaction())
                {
                  AcDb.Curve c = tr.GetObject(_id, AcDb.OpenMode.ForRead) as AcDb.Curve;
                  AcDb.BlockTableRecord btr = tr.GetObject(c.OwnerId, AcDb.OpenMode.ForWrite) as AcDb.BlockTableRecord;
                  double[] parrs = (double[])pars;
                  Array.Sort(parrs);
                  foreach (AcDb.Curve cc in c.GetSplitCurves(new DoubleCollection(parrs)))
                  {
                        btr.AppendEntity(cc);
                        tr.AddNewlyCreatedDBObject(cc, true);
                        objs.Add(cc.AcadObject);
                  }
                  tr.Commit();
                }
            }
            return objs.ToArray();
      }
    }
}VBA调用的代码
Sub tt()
    Dim tc As Object
    Set tc = Application.GetInterfaceObject("TlsCad.Common.Curve")
   
    Dim obj As Object, pnts(1)
    ThisDrawing.Utility.GetEntity obj, pnts(0)
    tc.Set obj.ObjectID
   
    pnts(0) = tc.GetClosestPointTo(pnts(0), False)
    pnts(1) = ThisDrawing.Utility.GetPoint()
    pnts(1) = tc.GetClosestPointTo(pnts(1), False)
   
    tc.SplitByPoints (pnts)
   
End SubVB调用的代码
Private Sub Command1_Click()
Dim app As Object
Set app = GetObject(, "AutoCad.Application")
Dim tcAs Object
Set tc = app.GetInterfaceObject("TlsCad.Common.Curve")
    Dim obj As Object, pnts(1)
    app.ActiveDocument.Utility.GetEntity obj, pnts(0)
    tc.Set obj.ObjectID
   
    pnts(0) = tc.GetClosestPointTo(pnts(0), False)
    pnts(1) = app.ActiveDocument.Utility.GetPoint()
    pnts(1) = tc.GetClosestPointTo(pnts(1), False)
   
    tc.SplitByPoints (pnts)
End SubC#调用的代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Interop.Common;
using System.Runtime.InteropServices;
using System.Reflection;
namespace ComTest
{
    public partial class Form1 : Form
    {
      public Form1()
      {
            InitializeComponent();
      }
      private void button1_Click(object sender, EventArgs e)
      {
            AcadApplication acApp = GetAcApp();
            object tc = acApp.GetInterfaceObject("TlsCad.Common.Curve");
            object acCurve;
            object[] pnts = new object;
            AcadDocument acDoc = acApp.ActiveDocument;
            acDoc.Utility.GetEntity(out acCurve, out pnts, "");
            InvokeMethod(tc, "Set", ((AcadEntity)acCurve).ObjectID);
            pnts = InvokeMethod(tc, "GetClosestPointTo", pnts, false);
            pnts = acDoc.Utility.GetPoint(pnts, "");
            pnts = InvokeMethod(tc, "GetClosestPointTo", pnts, false);
            InvokeMethod(tc, "SplitByPoints", new object{pnts});
      }
      public object InvokeMethod(object obj, string methodName, params object[] args)
      {
            Type t = obj.GetType();
            object res =
                t.InvokeMember(
                  methodName,
                  BindingFlags.Public | BindingFlags.InvokeMethod,
                  null,
                  obj,
                  args);
            return res;
      }

      public AcadApplication GetAcApp()
      {
            const string progID = "AutoCAD.Application.18";
            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 \"" +
                      progID + "\""
                  );
                }
            }
            return acApp;
      }
    }
}

gsteven 发表于 2009-12-15 21:56:00

<p>老大,问个简单的问题哈:</p><p>api.net编好dll之后,应该把这个dll放到cad安装目录下注册吧?</p><p>注册成功之后这个到cad安装目录下的dll能删除吗?</p><p>然后新建的应用程序在引用这个dll的时候是否复制本地呢?</p><p></p>

雪山飞狐_lzh 发表于 2009-12-16 07:45:00

<p>regasm TlsCad.Curve.dll /regfile /codebase</p><p>生成reg文件</p><p>如果不在安装目录注册,要更改reg文件中的目录信息</p><p>然后再运行注册</p><p>不需要引用dll的</p>

gsteven 发表于 2009-12-17 19:53:00

<p>但是kean的帖子里面的7楼和8楼都引用了dll,都有这一句using LoadableComponent;</p><p>不知道引用和不引用到底有什么区别,很困惑啊</p><p>另外他8楼的方法很难看懂,主要是他写了 </p><p>不知道这是干什么用的</p>
页: [1] 2
查看完整版本: 为.net程序集实现Com接口