cad.net Acad外部启动程序

说明

注意,com无法在同cad版本进行发送操作,它只会执行一次同版本,而不同版本则可以各执行一次.(这里可能带来错误,详情本文测试VB通过文档名获取Com实例)
为了解决这个问题,还可以利用进程句柄发送消息
以及跨进程通讯Remoting,网络通讯等等..

所以com用来启动cad还是蛮好的.

故事

首先是飞诗在问了一个问题: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版本号

上述如果是管理员,那么必须要精确版本.
32位程序无权读取64位程序的信息
读取注册表时候注意一下anyCPU/x86/x64

代码

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

[STAThread]
static void Main()
{
    try
    {
        var acad = new AcadProcess();
        acad.GetBoundingBox(); //如果有cad就直接利用当前,否则新开,然后获取包围盒

        //新开Acad
        //acad.Run(RunCadVer.Minimum);
        //acad.Run(RunCadVer.Maximum);
      
        var process = acad.ProcessArray;
        Debug.WriteLine($"当前启动了 {process.Length} 个Acad");
        foreach (var item in process)
            Debug.WriteLine($"进程id是:{item.Id}");      
    }
    catch (Exception ex)
    {
        throw new Exception("无法创建或附加到AutoCAD对象: " + ex.Message);
    }
}

AcadProcess.cs 指定版本启动cad

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace JoinBox.BasalRegistry
{
    public enum RunCadVer
    {
        Minimum,
        Maximum,
        All,
    }

    public class AcadProcess
    {
        #region 成员
        string ProgID = "AutoCAD.Application";
        const string _acad = "acad";
        List<AcadClsIdInfo> _acadClsIdInfos;

        /// <summary>
        /// 获取已经打开的cad程序
        /// </summary>
        public Process[] ProcessArray { get => Process.GetProcessesByName(_acad); }
        #endregion

        #region 构造
        public AcadProcess()
        {
            _acadClsIdInfos = new();

            //管理员模式必须要完整的版本号
            if (ProcessHelper.IsAdministrator())
            {
                var apks = AcadClsIdInfo.AcadProductKeys;
                if (apks.Count == 0)
                    return;
                ProgID += "." + apks[apks.Count - 1].Version;
            }
        }
        #endregion

        #region 方法
        /// <summary>
        /// 这里获取所有已经记录GUID的cad,没有记录将会由启动最新的时候加入
        /// </summary>
        void GetAcadComs()
        {
            AcadClsIdHelper.GetActiveAcadCom(_acadClsIdInfos);
#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 (_acadClsIdInfos.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
                _acadClsIdInfos = _acadClsIdInfos.OrderByDescending(item => item.VerNumber).ToList();
                for (int i = 0; i < _acadClsIdInfos.Count; i++)
                {
                    var dete = _acadClsIdInfos[i].VerNumber - (int)_acadClsIdInfos[i].VerNumber;//求小数部分
                    for (int j = i + 1; j < _acadClsIdInfos.Count; j++)
                    {
                        if (_acadClsIdInfos[i].VerNumber - _acadClsIdInfos[j].VerNumber <= dete)
                        {
                            _acadClsIdInfos.Remove(_acadClsIdInfos[j]);
                            j--;
                        }
                        else
                            break;
                    }
                }
            }
        }

        /// <summary>
        /// 启动Acad程序,指定版本
        /// </summary>
        /// <param name="runCadVer">启动什么版本</param>
        public void Run(RunCadVer runCadVer = RunCadVer.Minimum)
        {
            const string _acadexe = "\\acad.exe";
            string progID = "AutoCAD.Application.";

            //获取本机cad路径
            var apks = AcadClsIdInfo.AcadProductKeys;
            if (apks.Count == 0)
                return;

            string exePath;
            switch (runCadVer)
            {
                case RunCadVer.Minimum:
                {
                    var apk = apks[0];
                    progID += apk.Version;
                    exePath = apk.Location + _acadexe;
                    var acadApp = StartApplicat.Run(progID, exePath);
                    _acadClsIdInfos.Add(new AcadClsIdInfo(new Guid(), acadApp, apk));
                }
                break;
                case RunCadVer.Maximum:
                {
                    var apk = apks[apks.Count - 1];
                    progID += apk.Version;
                    exePath = apk.Location + _acadexe;
                    var acadApp = StartApplicat.Run(progID, exePath);
                    _acadClsIdInfos.Add(new AcadClsIdInfo(new Guid(), acadApp, apk));
                }
                break;
                case RunCadVer.All:
                {
                    foreach (var apk in apks)
                    {
                        var acadApp = StartApplicat.Run(progID + apk.Version, apk.Location + _acadexe);
                        exePath = apk.Location + _acadexe;
                        _acadClsIdInfos.Add(new AcadClsIdInfo(new Guid(), acadApp, apk));
                    }
                }
                break;
            }
        }

        /// <summary>
        /// 通过cad的com进行反射调用VBA函数获取包围盒
        /// </summary>
        public void GetBoundingBox()
        {
            //使用 _acadComs.Add 将导致重复获取了com和启动时的,所以消重.
            GetAcadComs();//遍历当前
            if (_acadClsIdInfos.Count == 0)
            {
                Run();
                //GetAcadComs();//可以看到Run加入和ClsId加入的区别,用来测试消重是否正确
            }
            _acadClsIdInfos = _acadClsIdInfos.Distinct(new AcadClsIdInfoDistinct()).ToList();

            var comObject = Type.GetTypeFromProgID(ProgID);
            if (comObject == null)
                throw new ArgumentNullException($"本机不存在:{ProgID}");

            foreach (var acadClsIdInfo in _acadClsIdInfos)
            {
                var acadAppCom = acadClsIdInfo.Com;
#if true
                //参数
                object[] args = new object[1];
                //设置需要设置的参数值
                args[0] = true;
                //设置属性-可视,显示窗体
                comObject.InvokeMember("Visible", BindingFlags.SetProperty, null, acadAppCom, args);

                //获取属性
                object comAcDoc = comObject.InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, acadAppCom, null);
                object comAcMsSpace = comObject.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 = comObject.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.
                comObject.InvokeMember("GetBoundingBox",
                    BindingFlags.SuppressChangeType | BindingFlags.InvokeMethod,
                    null, comAcLine, pts, new ParameterModifier[] { paramMod }, null, null);

                //全屏显示
                comObject.InvokeMember("ZoomAll", BindingFlags.InvokeMethod, null, acadAppCom, null);
#else
                //c#4等效代码
                acadAppCom.ZoomAll();
                dynamic acadDoc = acadAppCom.ActiveDocument;
                if (acadDoc != null)
                {
                    //acadDoc.SendCommand("_.Line 100,100 300,300  ");
                    dynamic acMsSpace = acadDoc.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() + "\n" + 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]}");
            }
        }
        #endregion
    }
}

AcadRegedit.cs 注册表

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Win32;

namespace JoinBox.BasalRegistry
{
    public class AcadProductKey
    {
        /// <summary>
        /// 注册表位置
        /// </summary>
        public RegistryKey ProductKey;
        /// <summary>
        /// cad安装路径
        /// </summary>
        public string Location;
        /// <summary>
        /// cad名称
        /// </summary>
        public string ProductName;
        /// <summary>
        /// cad版本号 17.1之类的
        /// </summary>
        public string Version;
        public double VerNumber
        {
            get
            {
                var strVer = Version.Split('.');
                double.TryParse(strVer[0] + "." + strVer[1], out double ver);
                return ver;
            }
        }
        public Guid Guid;
    }

    internal class AcadRegedit
    {
        /// <summary>
        /// 注册表路径,已经最小到最大排序
        /// </summary>
        internal static List<AcadProductKey> AcadProductKeys { get; }

        static AcadRegedit()
        {
            AcadProductKeys = GetAcadProductKeys();
            GetGuidKeys(AcadProductKeys);
        }

        /// <summary>
        /// 获取Guid
        /// </summary>
        /// <param name="apk"></param>
        static void GetGuidKeys(List<AcadProductKey> apk)
        {
            const string appReg = "AutoCAD.Application";
            const string appRegDot = appReg + ".";

            //var fmts = new string[] { @"Software\Classes", @"Software\Wow6432Node\Classes" };
            var fmts = new string[] { @"Software\Classes" };//只需要在32位上面找

            foreach (var fmt in fmts)
            {
                var classes = Registry.LocalMachine.OpenSubKey(fmt);
                if (classes == null)
                    continue;

                foreach (string verReg in classes.GetSubKeyNames())
                {
                    try
                    {
                        if (verReg.Contains(appReg))
                        {
                            var app = classes.OpenSubKey(verReg);
                            if (app == null)
                                continue;
                            var clsid = app.OpenSubKey("CLSID");
                            if (clsid == null)
                                continue;

                            var verStr = verReg.Replace(appRegDot, string.Empty);
                            double.TryParse(verStr, out double verNumber);
                            foreach (var item in apk)
                            {
                                if (item.VerNumber == verNumber)
                                {
                                    item.Guid = new Guid(clsid.GetValue(null).ToString());
                                    break;
                                }
                            }
                        }
                    }
                    catch
                    { }
                }
            }
        }

        /// <summary>
        /// 获取注册表版本信息和安装路径
        /// </summary>
        /// <returns></returns>
        static List<AcadProductKey> GetAcadProductKeys()
        {
            var adsk = Registry.CurrentUser.OpenSubKey(@"Software\Autodesk\AutoCAD");
            if (adsk == null)
                return null;

            var apk = new List<AcadProductKey>();
            foreach (string ver in adsk.GetSubKeyNames())
            {
                try
                {
                    var emnuAcad = adsk.OpenSubKey(ver);
                    var curver = emnuAcad.GetValue("CurVer");
                    if (curver == null)
                        continue;
                    string app = curver.ToString();
                    string fmt = @"Software\Autodesk\AutoCAD\{0}\{1}";
                    emnuAcad = Registry.LocalMachine.OpenSubKey(string.Format(fmt, ver, app));
                    if (emnuAcad == null)
                    {
                        fmt      = @"Software\Wow6432Node\Autodesk\AutoCAD\{0}\{1}";
                        emnuAcad = Registry.LocalMachine.OpenSubKey(string.Format(fmt, ver, app));
                    }

                    var acadLocation = emnuAcad.GetValue("AcadLocation");
                    if (acadLocation == null)
                        continue;
                    string location = acadLocation.ToString();
                    if (File.Exists(location + "\\acad.exe"))
                    {
                        var produ = emnuAcad.GetValue("ProductName");
                        var release = emnuAcad.GetValue("Release");
                        if (release == null)
                            continue;
                        var strVer = release.ToString().Split('.');
                        var pro = new AcadProductKey()
                        {
                            ProductKey  = emnuAcad,
                            Location    = location,
                            ProductName = produ?.ToString(),
                            Version     = strVer[0] + "." + strVer[1],

                        };
                        apk.Add(pro);
                    }
                }
                catch { }
            }
            apk = apk.OrderBy(cad => cad.Version).ToList();
            return apk;
        }
    }
}

AcadClsIds.cs 收集Acad的Com接口

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace JoinBox.BasalRegistry
{
    /// <summary>
    /// Linq Distinct 消重
    /// </summary>
    public class AcadClsIdInfoDistinct : IEqualityComparer<AcadClsIdInfo>
    {
        public bool Equals(AcadClsIdInfo a, AcadClsIdInfo b)
        {
            return a.Version == b.Version ||
                   a.Version == b.AcadProduct.Version;
        }

        public int GetHashCode(AcadClsIdInfo obj)
        {
            return base.GetHashCode();
        }
    }

    public class AcadClsIdInfo
    {
        /// <summary>
        /// Acad产品注册表信息
        /// </summary>
        public static List<AcadProductKey> AcadProductKeys = AcadRegedit.AcadProductKeys;
        AcadProductKey _AcadProduct;
        public AcadProductKey AcadProduct
        {
            get
            {
                if (_AcadProduct == null)
                    _AcadProduct = AcadProductKeys.First(a => a.Version == Version);
                return _AcadProduct;
            }
        }

        public double VerNumber
        {
            get
            {
                double.TryParse(Version, out double verdouble);
                return verdouble;
            }
        }
        string _Version;
        public string Version
        {
            get
            {

                if (_Version == null)
                {
                    foreach (var item in AcadProductKeys)
                    {
                        if (item.Guid == Guid)
                        {
                            _Version = item.Version;
                            break;
                        }
                    }
                }
                return _Version;
                // return AcadClsId.GetAcadVer(Guid);
            }
        }
        public Guid Guid { get; }
        public object Com { get; }

        public AcadClsIdInfo(Guid acadClsId, object acadCom, AcadProductKey acadProduct)
        {
            Guid         = acadClsId;
            Com          = acadCom;
            _AcadProduct = acadProduct;
        }
    }

    public class AcadClsIdHelper
    {
        //参考自 http://www.cadgj.com/?p=297
        [DllImport("ole32.dll", EntryPoint = "CreateBindCtx")]
        static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);

        [DllImport("ole32.dll", EntryPoint = "GetRunningObjectTable")]
        static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);

        /// <summary>
        /// 遍历进程
        /// </summary>
        /// <param name="action">程序名,com接口</param>
        public static void GetAllInstances(Action<string, object> action)
        {
            int retVal = GetRunningObjectTable(0, out IRunningObjectTable rot);
            if (retVal == 0)
            {
                rot.EnumRunning(out IEnumMoniker enumMoniker);
                var moniker = new IMoniker[1];
                while (enumMoniker.Next(1, moniker, IntPtr.Zero) == 0)
                {
                    CreateBindCtx(0, out IBindCtx bindCtx);
                    moniker[0].GetDisplayName(bindCtx, null, out string displayName);
                    rot.GetObject(moniker[0], out object comObj);
                    if (comObj == null)
                        continue;
                    action.Invoke(displayName, comObj);
                }
            }
        }


        /// <summary>
        /// 获取已经启动的CAD进程的com
        /// </summary>
        /// <param name="apps">返回已经存在的参数</param>
        /// <param name="acadClsId">指定版本</param>
        public static void GetActiveAcadCom(List<AcadClsIdInfo> apps, Guid? acadClsId = null)
        {
            List<Guid> guids = new();
            if (acadClsId == null)
            {
                guids = AcadClsIdInfo.AcadProductKeys.Select(a => a.Guid).ToList();
                //guids = AcadClsId.AcadClsIds.Values.ToList();
            }
            else
            {
                guids.Add(acadClsId.Value);
            }

            GetAllInstances((displayName, comObj) => {
#if DEBUG
                //不启动cad的时候运行一次,看看有什么GUID,然后再启动一个cad对比就知道了
                Debug.WriteLine("****这个进程  " + displayName);
#endif
                var guid = ConversionGuid(displayName);
                if (guid != null && guids.Contains(guid.Value))
                {
                    //17.1存在的时候17也会存在,所以会有两个obj被保存
                    apps.Add(new AcadClsIdInfo(guid.Value, comObj, null));
                }
            });
        }

        /// <summary>
        /// 显示名称转为Guid
        /// </summary>
        /// <param name="displayName">显示名称 "!{6AB55F46-2523-4701-2222-B226F46252BA}"</param>
        /// <returns></returns>
        static Guid? ConversionGuid(string displayName)
        {
            if (string.IsNullOrEmpty(displayName))
                return null;
            Guid? guid = null;
            try
            {
                displayName = displayName.Substring(2, displayName.Length - 3);
                guid        = new Guid(displayName);
            }
            catch { }
            return guid;
        }
    }
}

AcadStart.cs 启动cad

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

#if NET50
using Microsoft.VisualBasic;
#endif

namespace JoinBox.BasalRegistry
{
    public static class StartApplicat
    {
        public static object Run(string progID, string exePath)
        {
            //处理 GetActiveObject 在电脑睡眠之后获取就会失败.所以要 ProcessStartInfo
            //https://blog.csdn.net/yuandingmao/article/details/5558763?_t_t_t=0.8027849649079144
            //string progID = "AutoCAD.Application.17.1";
            //string exePath = @"C:\Program Files (x86)\AutoCAD 2008\acad.exe";

            object acadApp = null;
            acadApp = AcadBlockingStart(progID);
            if (acadApp != null)
                return acadApp;

            // var psi = new ProcessStartInfo(exePath, "/p myprofile");//使用cad配置,myprofile是配置名称,默认就不写
            var psi = new ProcessStartInfo(exePath, "/nologo")
            {
                WorkingDirectory = Environment.GetEnvironmentVariable("TEMP")//这里什么路径都可以的
            };
            var pr = Process.Start(psi);
            pr.WaitForInputIdle();//这里并不会阻塞

            int qidong = 0;
            while (acadApp == null)
            {
                try
                {
                    acadApp = AcadBlockingStart(progID);
                }
                catch
                {
                    Application.DoEvents();
                }
                if (qidong == 20)
                    throw new ArgumentNullException("启动失败,错误计数已达最高");

                ++qidong;
                Thread.Sleep(500);
            }
            return acadApp;
        }


        /// <summary>
        /// 启动cad(阻塞)
        /// </summary>
        /// <returns></returns>
        public static object AcadBlockingStart(string ProgID, string path = null)
        {
            object acadApp = null;
#if NET50
            //两种方法,都会启动一个新的Acad.
            int startWay = 1;//启动方式
            if (startWay == 1)
            {
                //阻塞启动
                acadApp = Interaction.CreateObject(ProgID);
            }
            else if (startWay == 2)
            {
                //阻塞启动
                var comObjectName = Type.GetTypeFromProgID(ProgID);
                if (comObjectName == null)
                    throw new ArgumentNullException($"本机不存在:{ProgID}");
                acadApp = Activator.CreateInstance(comObjectName);
            }
#else
            //阻塞启动
            acadApp = Marshal.GetActiveObject(ProgID);
#endif
            return acadApp;
        }

        /// <summary>
        /// 启动cad(非阻塞)
        /// </summary>
        /// <returns>进程id</returns>
        public static int AcadAntiBlockingStart(string exePath)
        {
#if NET50
            //不阻塞启动
            //var exePath   = @"C:\Program Files\Autodesk\AutoCAD 2021\acad.exe";
            //exePath       = @"C:\Program Files (x86)\AutoCAD 2008\acad.exe";
            int acadId = Interaction.Shell(exePath, AppWinStyle.NormalFocus); //正常大小启动,返回进程id
            return acadId;
#else
            // var psi = new ProcessStartInfo(exePath, "/p myprofile");//使用cad配置,myprofile是配置名称,默认就不写
            var psi = new ProcessStartInfo(exePath, "/nologo")
            {
                WorkingDirectory = Environment.GetEnvironmentVariable("TEMP")//这里什么路径都可以的
            };
            var pr = Process.Start(psi);
            //  pr.WaitForInputIdle();//这里并不会阻塞
            return pr.Id;
#endif
        }

    }
}

ProcessHelper.cs 进程工具类

using System.Security.Principal;

namespace JoinBox.BasalRegistry
{
    public static class ProcessHelper
    {
        /// <summary>
        /// 判断当前程序是否通过管理员运行
        /// </summary>
        /// <returns></returns>
        public static bool IsAdministrator()
        {
            var current = WindowsIdentity.GetCurrent();
            var windowsPrincipal = new WindowsPrincipal(current);
            return windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
        }
    }
}

Acad所有版本存放在这个注册表位置

计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\Hardcopy

var ackey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy", false);
var values = ackey.GetValueNames();

测试VB通过文档名获取Com实例

cad内部调用com接口是可以区分同cad版本的,但是外部调用要经过系统的com服务.

南胜告诉我们,
在VB里面调用Microsoft.VisualBasic.Interaction.GetObject(文档路径)传入一个已经被程序打开的文档路径,是可以获取同版本cad的com的.
而没有保存文档怎么办?
答案是我不知道,但是我也大胆的猜测一下,cad打开dwt模板的时候会在系统临时文件夹创建一个缓存文件,我们找到这个缓存文件然后跟开图一样Interaction.GetObject问询它占用的程序com?

有个问题

测试时候发现断点会出现一个"把其他cad的文档抓过来第一个开的cad"上面,然后呈现只读文档,而直接运行就不会.

我大胆的假设一下:
com需要连续的报文,断点调试等于服务器收不到信号,程序认为没打开这个文档,帮你开一个,
然后触发了dwg打开的流程=>
先再注册表找.dwg,再经过<桌子的dwg启动服务>,然后让已经启动的<第一个开的cad>承接打开这个dwg.

测试代码

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
#if NET50
using Microsoft.VisualBasic;
#endif

namespace 测试_exe发送cad
{
    static class Program
    {
        [STAThread]
        static void Main()
        {          
             try
            {
                // 开启两个 已经保存文档 的同版本cad,
                // 然后启动这个测试代码,
                // 第一个启动的cad上面有 "第二个cad的文档名称-只读" (断点触发,并且切换一下文档才会冒出[只读]的文字)
              
                //获取所有acad的进程句柄
                var acadPros = Process.GetProcessesByName("acad");
                foreach (var acadPro in acadPros)
                {
                    //fn = "D:\\桌面\\asdasdasd.dwg"
                    var fn =
                        acadPro.MainWindowTitle.GetSpliterRightText("[").Replace("]", "").Trim();
                    if (File.Exists(fn)) //跳过 drawing*  2008测试不需要,高版本需要
                    {
                        dynamic acadCom = Interaction.GetObject(fn);
                        dynamic acadApp = acadCom.Application;
                        foreach (var doc in acadApp.Documents)
                        {
                            string fullName = doc.FullName;
                            string name = doc.Name;
                            var str = $"****进程{acadPro.Id}::FullName:{fullName}, Name:{name}";
                            bool isReadOnly = doc.ReadOnly;//doc只读
                            if (isReadOnly)
                                Debug.WriteLine(str + "-------只读文档!");
                            else
                                Debug.WriteLine(str + "==========");
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
                Debugger.Break();
            }
        }
    }

    public static class StrSpliterHelper
    {
        /// <summary>
        /// 字符串有分隔符
        /// </summary>
        /// <param name="str"></param>
        /// <param name="spliter"></param>
        /// <returns></returns>
        public static bool HasSpliter(this string str, string spliter)
            => str.Contains(spliter);

        /// <summary>
        /// 获取分割符左侧的部分
        /// </summary>
        /// <param name="str"></param>
        /// <param name="spliter"></param>
        /// <returns></returns>
        public static string GetSpliterLeftText(this string str, string spliter)
            => str.HasSpliter(spliter) ?
            str.Substring(0, str.IndexOf(spliter, StringComparison.Ordinal)) : str;

        /// <summary>
        /// 获取分隔符右侧的字符串
        /// </summary>
        /// <param name="str"></param>
        /// <param name="spliter"></param>
        /// <returns></returns>
        public static string GetSpliterRightText(this string str, string spliter)
            => str.HasSpliter(spliter) ?
            str.Substring(str.IndexOf(spliter, StringComparison.Ordinal) + 1,
                str.Length - str.IndexOf(spliter, StringComparison.Ordinal) - 1)
            : str;
    }
}

(完)

posted @ 2019-08-20 09:59  惊惊  阅读(2346)  评论(0编辑  收藏  举报