• cad.net 反射com获取包围盒从参数获取返回的数据+net5不能用Marshal.GetActiveObject解决方案


    故事

    首先是飞诗在问了一个问题:Acad2007的com包围盒无法正确获取文字的包围盒,问有没有其他方法?
    但是他测试了lisp的获取是正确的,所以他想反射调用里面内置的.

    而他会反射,但是获取不到在参数传回返回值.edata解决了这个问题,
    他在《精通.NET互操作:P/Invoke,C++ Interop和COM.Interop》 黄际洲 崔晓源 编著
    264页中找到一段从参数返回结果的.

    然后我测试的时候,发现net5桌面程序不能用:Marshal.GetActiveObject真是一个问题接一个..

    这可怎么办好呢?
    虽然我在学net5的教学中知道了这个,但是它没有给我解决方案.
    然后南胜提醒了我一下,有using Microsoft.VisualBasic.Interaction.CreateObject可以用.
    为此我完成了之前没有完成的东西:com启动Acad

    在测试期间我还发现Acad08启动之后获取com对象,会获取到17.1和17两个对象,因此才知道了我之前做的com发送命令为什么发送了两次...

    发现启动cad的几种方式

    1,注册表获取acad.exe路径启动:这样
    2,com版本号启动,指定精确版本:"AutoCAD.Application.17.1".
    3,com版本号启动,指定大版本号,启动的可能是17系列的,但是2007必然是这个啊:"AutoCAD.Application.17"
    4,com不指定版本号,启动最后一次的启动:"AutoCAD.Application".
    5,根据guid启动,可以找注册表上面对应的exe,也可以像下面一样找com版本号

    开干

    写一个net5的控制台调用这个,当然了,你喜欢低版本也可以...

    new ReflectionAcad().GetBoundingBox();
    

    然后复制粘贴这个就好了,各种的com技术都蛮有意思的.

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Runtime.InteropServices.ComTypes;
    using System.Windows;
    using Microsoft.VisualBasic;
    
    namespace 测试_exe发送cad
    {
        public class AcadClsId
        {
            //var acadGuid = AcadClsId.GetAcadGuid("17.1");
            //var bvv = AcadClsId.GetAcadVer("6AB55F46-2523-4701-2222-B226F46252BA");
    
            public static Dictionary<string, Guid> AcadClsIds = new Dictionary<string, Guid>()
            {
               {"15",   new Guid("8E75D911-3D21-11d2-85C4-080009A0C626")},
               {"16",   new Guid("1365A45F-0C8F-4806-A26A-6B22AD37EC66")},
               {"16.1", new Guid("FC280999-88C6-4499-9622-3B795A8B4A5F")},
               {"16.2", new Guid("F131FB74-0E12-4533-8091-D71FE9CCD91D")},
               {"17",   new Guid("28B7AA99-C0F9-4C47-995E-8A8D729603A1")},
               {"17.1", new Guid("6AB55F46-2523-4701-A912-B226F46252BA")},
               {"17.2", new Guid("2F1F7574-ECCA-4361-B4DE-C411BF7EEE23")},
               {"18",   new Guid("6D7AE628-FF41-4CD3-91DD-34825BB1A251")},
               {"18.1", new Guid("C92FB640-AD4D-498A-9979-A51A2540C977")},
               {"18.2", new Guid("B77E471C-FBF3-4CB5-880F-D7528AD4B349")},
               {"19",   new Guid("BD0DEB94-63DB-4392-9420-6EEE05094B1F")},
               {"19.1", new Guid("7DE1BE5C-CEBA-4F1D-ACBC-9CE11EE9A2A1")},
               {"20",   new Guid("0B628DE4-07AD-4284-81CA-5B439F67C5E6")},
               {"20.1", new Guid("5370C727-1451-4700-A960-77630950AF6D")},
               {"21",   new Guid("0D327DA6-B4DF-4842-B833-2CFF84F0948F")},
               {"22",   new Guid("9AAF0EB6-42D8-46C1-A2EF-679511B37A0D")},
               {"23",   new Guid("4AC6DFE1-607B-45B2-B289-D7FBCD44169C")},
               {"23.1", new Guid("D1DE6864-2236-48B7-99C3-D29C757903A4")},
            };
    
            /// <summary>
            /// 通过guid找cad版本号
            /// </summary>
            /// <param name="clsid"></param>
            /// <returns>不存在返回null</returns>
            public static string GetAcadVer(string clsid)
            {
                return GetAcadVer(new Guid(clsid));
            }
    
            /// <summary>
            /// 通过guid找cad版本号
            /// </summary>
            /// <param name="clsid"></param>
            /// <returns>不存在返回null</returns>
            public static string GetAcadVer(Guid clsid)
            {
                return AcadClsIds.FirstOrDefault(q => q.Value == clsid).Key;
            }
    
            /// <summary>
            /// 通过cad版本号找guid
            /// </summary>
            /// <param name="cadVer"></param>
            /// <returns></returns>
            public static Guid GetAcadGuid(string cadVer)
            {
                AcadClsIds.TryGetValue(cadVer, out Guid value);
                return value;
            }
    
        }
    
    
        public class AcadCom
        {
    #if DEBUG2
            public AcadCom(string acadClsId)
            {
                this.Ver = acadClsId;
            }
    #endif 
            public AcadCom(Guid acadClsId, object acadCom)
            {
                this.Com = acadCom;
                this.Guid = acadClsId;
                this.Ver = AcadClsId.GetAcadVer(acadClsId);
            }
            public double VerNumber
            {
                get
                {
                    double.TryParse(Ver, out double verdouble);
                    return verdouble;
                }
            }
            public string Ver { get; }
            public Guid Guid { get; }
            public object Com { get; }
        }
    
        //http://www.cadgj.com/?p=297
        public partial class AcadComTool
        {
            [DllImport("ole32.dll", EntryPoint = "CreateBindCtx")]
            static extern int _CreateBindCtx(int reserved, out IBindCtx ppbc);
    
            /// <summary>
            /// 获取已经启动的CAD进程的com
            /// </summary>
            /// <param name="apps"></param>
            public static void GetActiveAcadCom(List<AcadCom> apps, Guid? acadClsId = null)
            {
                _CreateBindCtx(0, out IBindCtx pbc);
                pbc.GetRunningObjectTable(out IRunningObjectTable pprot);
    
                pprot.EnumRunning(out IEnumMoniker ppenumMoniker);
                ppenumMoniker.Reset();
                IMoniker[] rgelt = new IMoniker[1];
                rgelt[0] = null;
                int rc = ppenumMoniker.Next(1, rgelt, IntPtr.Zero);
    
                List<Guid> guids = new();
                if (acadClsId == null)
                {
                    guids = AcadClsId.AcadClsIds.Values.ToList();
                }
                else
                {
                    guids.Add(acadClsId.Value);
                }
    
                while (rgelt[0] != null)
                {
                    rgelt[0].GetDisplayName(pbc, null, out string ppszDisplayName);
    #if DEBUG
                    //不启动cad的时候运行一次,看看有什么GUID,然后再启动一个cad对比就知道了
                    Debug.WriteLine(ppszDisplayName);
    #endif 
                    var guid = ConversionGuid(ppszDisplayName);
                    if (guid != null && guids.Contains(guid.Value))
                    {
                        //17.1存在的时候17也会存在,所以会有两个obj被保存
                        pprot.GetObject(rgelt[0], out object obj);
                        if (obj == null)
                        {
                            continue;
                        }
                        apps.Add(new AcadCom(guid.Value, obj));
                    }
                    rgelt[0] = null;
                    rc = ppenumMoniker.Next(1, rgelt, IntPtr.Zero);
                }
            }
    
            /// <summary>
            /// 转为Guid: "!{6AB55F46-2523-4701-2222-B226F46252BA}"
            /// </summary>
            /// <param name="ppszDisplayName"></param>
            /// <returns></returns>
            static Guid? ConversionGuid(string ppszDisplayName)
            {
                //转为GUID再匹配
                if (string.IsNullOrEmpty(ppszDisplayName))
                {
                    return null;
                }
                //c#将此建议为下句 ppszDisplayName = ppszDisplayName.Substring(2, ppszDisplayName.Length - 3);
                ppszDisplayName = ppszDisplayName[2..^1];
    
                Guid guid;
                try
                {
                    guid = new Guid(ppszDisplayName);
                }
                catch
                {
                    return null;
                }
                return guid;
            }
        }
    
    
        public partial class ReflectionAcad
        {
            const string ProgID = "AutoCAD.Application";
    
            /// <summary>
            /// com方式启动cad
            /// </summary>
            /// <returns></returns>
            private object StartAcad()
            {
                object acAcadApp = null;  
    #if !NET50
                dynamic acAcadApp = Marshal.GetActiveObject(progID); 
    #else
                //两种方法,都会启动一个新的Acad.
    #if true2
                //**不阻塞启动**
                var path = @"C:Program FilesAutodeskAutoCAD 2021acad.exe";
                path = @"C:Program Files (x86)AutoCAD 2008acad.exe";
                int acadid = Interaction.Shell(path, AppWinStyle.NormalFocus); //正常大小启动 ,返回进程id
    #else
                //**阻塞启动**                 
                // acAcadApp = Interaction.GetObject("", ProgID);
                acAcadApp = Interaction.CreateObject(ProgID);
    #endif
    #endif    
                //acAcadApp = Activator.CreateInstance(comObjectName);
    
                return acAcadApp;
            }
    
            /// <summary>
            /// 通过cad的com进行反射调用VBA函数获取包围盒
            /// </summary>
            public void GetBoundingBox()
            {
                //与指定 ProgID 关联的类型,即获取相应的Com对象       
                var comObjectName = Type.GetTypeFromProgID(ProgID);
                if (comObjectName == null)
                {
                    MessageBox.Show($"本机不存在:{ProgID}");
                    return;
                }
    
                List<AcadCom> acadComs = new();
                AcadComTool.GetActiveAcadCom(acadComs);
                if (acadComs.Count == 0)
                {
                    // Acad没有启动,那就去启动它
                    var acAcadApp2 = StartAcad();
                    if (acAcadApp2 == null)
                    {
                        MessageBox.Show($"无法打开程序:{ProgID}");
                        return;
                    }
                    acadComs.Add(new AcadCom(new Guid(), acAcadApp2));//此处GUID是无意义的
                }
    
    #if DEBUG2
                acadComs.Add(new AcadCom("16"));
                acadComs.Add(new AcadCom("16.1"));
                acadComs.Add(new AcadCom("16.2"));
                acadComs.Add(new AcadCom("15.1"));
                acadComs.Add(new AcadCom("18.2"));
                acadComs.Add(new AcadCom("17.5"));
                acadComs.Add(new AcadCom("12.5"));
                acadComs.Add(new AcadCom("12.4"));
                acadComs.Add(new AcadCom("12"));
    #endif 
                if (acadComs.Count > 1)//启动cad的时候会是1,减少运算
                {
                    //开启了Acad08,那么它是17.1,此时17也会存在,所以会有两个obj被保存,需要避免发送两次命令到同一个cad内
                    //因此,此处需要过滤同系列号的,举出最高版本
                    //排序17.2>17.1>17>16.1>16,将17.2保留,删除17.1>17,保留16.1,删除16
                    acadComs = acadComs.OrderByDescending(item => item.VerNumber).ToList();
                    for (int i = 0; i < acadComs.Count; i++)
                    {
                        double dete = acadComs[i].VerNumber - (int)acadComs[i].VerNumber;//求小数部分
                        for (int j = i + 1; j < acadComs.Count; j++)
                        {
                            if (acadComs[i].VerNumber - acadComs[j].VerNumber <= dete)
                            {
                                acadComs.Remove(acadComs[j]);
                                j--;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                }
    
                foreach (var comClass in acadComs)
                {
                    var acAcadApp = comClass.Com;
    #if true
                    //参数
                    object[] args = new object[1];
                    //设置需要设置的参数值
                    args[0] = true;
                    //设置属性-可视,显示窗体
                    comObjectName.InvokeMember("Visible", BindingFlags.SetProperty, null, acAcadApp, args);
    
                    //获取属性
                    object comAcDoc = comObjectName.InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, acAcadApp, null);
                    object comAcMsSpace = comObjectName.InvokeMember("ModelSpace", BindingFlags.GetProperty, null, comAcDoc, null);
                    //调用VBA函数也就是com暴露的函数,画在"激活的文档"的"模型空间"然后输入画一条线的坐标数据.
                    object[] lines = new object[] { new double[] { 100, 100, 0 }, new double[] { 300, 300, 0 } };
                    object comAcLine = comObjectName.InvokeMember("AddLine", BindingFlags.InvokeMethod, null, comAcMsSpace, lines);
    
                    //pts就是包围盒返回的点集
                    object[] pts = new object[2] { null, null };
    
                    //由于需要从参数中返回结果,所以需要设置 ParameterModifier 作用在 InvokeMember 上
                    var paramMod = new ParameterModifier(2);
                    paramMod[0] = true;//设置为true才能改写
                    paramMod[1] = true;
    
                    //求得这条线的包围盒,返回给pts.
                    comObjectName.InvokeMember("GetBoundingBox",
                        BindingFlags.SuppressChangeType | BindingFlags.InvokeMethod,
                        null, comAcLine, pts, new ParameterModifier[] { paramMod }, null, null);
    
                    //全屏显示
                    comObjectName.InvokeMember("ZoomAll", BindingFlags.InvokeMethod, null, acAcadApp, null);
    #else
                    //c#4等效代码
                    acAcadApp.ZoomAll();
                    dynamic acAcadDoc = acAcadApp.ActiveDocument;
                    if (acAcadDoc != null)
                    {
                        //acAcadDoc.SendCommand("_.Line 100,100 300,300  ");
                        dynamic acMsSpace = acAcadDoc.ModelSpace;
                        double[] p1 = new double[3] { 100.0, 100.0, 0.0 };
                        double[] p2 = new double[3] { 300.0, 300.0, 0.0 };
    
                        dynamic acLine = acMsSpace.AddLine(p1, p2);
                        object ptMin = new object();
                        object ptMax = new object();
                        acLine.GetBoundingBox(out ptMin, out ptMax);
                        MessageBox.Show(ptMin.ToString() + "
    " + ptMax.ToString());
                    }
    #endif 
                    var a = (double[])pts[0];
                    var b = (double[])pts[1];
                    Debug.WriteLine($"MinPoint={a[0]},{a[1]},{a[2]}");
                    Debug.WriteLine($"MaxPoint={b[0]},{b[1]},{b[2]}");
                }
            }
        }
    }
    
    
    

    (完)

  • 相关阅读:
    计算机网络基础:TCP运输连接管理(三次握手 + 四次挥手)
    Zookeeper集群及配置
    Maven安装及配置
    SpringMVC拦截器+Spring自定义注解实现权限验证
    Spring之AOP配置
    设置Google浏览器不缓存JS
    数据加密之AES
    SpringMVC配置多个自定义拦截器
    利用Zookeeper实现分布式锁
    数据加密之RSA
  • 原文地址:https://www.cnblogs.com/JJBox/p/14588762.html
Copyright © 2020-2023  润新知