zoukankan      html  css  js  c++  java
  • 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]}");
                }
            }
        }
    }
    
    
    

    (完)

  • 相关阅读:
    关于js计算非等宽字体宽度的方法
    [NodeJs系列]聊一聊BOM
    Vue.js路由管理器 Vue Router
    vue 实践技巧合集
    微任务、宏任务与Event-Loop
    事件循环(EventLoop)的学习总结
    Cookie、Session和LocalStorage
    MySQL 树形结构 根据指定节点 获取其所在全路径节点序列
    MySQL 树形结构 根据指定节点 获取其所有父节点序列
    MySQL 创建函数报错 This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators
  • 原文地址:https://www.cnblogs.com/JJBox/p/14588762.html
Copyright © 2011-2022 走看看