cad.net 重置cad之后创建文档出错,拦截cad致命错误

说明

在Acad08重置配置,然后直接创建文档,那么你会得到一个致命错误.
然后你会重启cad,再创建文件,这个时候就不会产生致命错误.
这个不起眼的bug似乎没有人去理它..

直到我制作了文档栏,
在文档栏上面创建文档,然后弹出了一个致命错误...
为了修复这个致命错误,我尝试了以下几个步骤.

方案一:自写模板

// 最后使用的模板
// 设置默认选中模板路径
var lastTemplate = CadSystem.Getvar("LastTemplate");
if (!File.Exists(lastTemplate)) {
    var templatePath = CadSystem.Getvar("TemplatePath");
    lastTemplate = templatePath + "\\acad.dwt";
}

// System.Runtime.InteropServices.COMException:“内部应用程序出错。”
var sa = new Autodesk.AutoCAD.Windows.OpenFileDialog(
    "选择模板", lastTemplate, "dwt", "",
    OpenFileDialogFlags.AllowAnyExtension | OpenFileDialogFlags.ForceDefaultFolder);
if (sa.ShowDialog() == System.Windows.Forms.DialogResult.OK) {  
    Acap.DocumentManager.Add(sa.Filename);
}

方案二:发送命令

我分析了一下,
Acap.DocumentManager.Add()会弹报错,
证明API创建文档是不行(后来证明另有用处...)

正常也应该直接调用cad命令方式,毕竟官方的方法可以避免错误...
于是乎,我改用了

var doc = Acap.DocumentManager.MdiActiveDocument;
doc?.SendStringToExecute("_new\n", false, true, true);
// 第二个参数true时,
// 命令必须有CommandFlags.Session,否则容易致命错误

但是遇到一个尴尬的事情,
关闭全部文档之后,不存在MdiActiveDocument
此时发送新建命令是错误的.

方案三:Win32API发送消息

e大协助了我,利用了win32API的方式来处理:

// 下面两个值都行的
WinApi.SendMessage(Acap.MainWindow.Handle, (int)MsgType.WM_COMMAND, (IntPtr)18, IntPtr.Zero);//qnew
WinApi.SendMessage(Acap.MainWindow.Handle, (int)MsgType.WM_COMMAND, (IntPtr)57600, IntPtr.Zero);//new

子类化拦截

当我实现了方案三的时候,
由于实现过<子类化窗口>来拦截cad的消息,
突然间拦截了一个错误:
Autodesk.AutoCAD.Runtime.Exception:“eFilerError”

而整个步骤就是:
重置配置(op)--创建cad文档(ctrl+n)--截获了这个错误,
非常的干脆利落得到.

img

封装

子类化cad主窗体--拦截cad创建文档命令,
如果不回调,就相当ban掉了命令,
同时等价于拦截了致命错误,贼好玩:

namespace JoinBox.Forms;
using System;
using System.Windows.Forms;

/// <summary>
/// 窗口控件子类化
/// </summary>
public class NativeCallProc : NativeWindow, IDisposable
{
    Func<Message, bool>? _WndProcAction;

    /// <summary>
    /// 窗口控件子类化<br/>
    /// 声明一定要写到类成员上,否则导致GC不确定的释放,从而触发析构
    /// </summary>
    /// <param name="intPtr">窗体句柄</param>
    public NativeCallProc(IntPtr intPtr)
    {
        this.AssignHandle(intPtr);
    }

    /// <summary>
    /// 消息循环:传委托进去不断替换
    /// </summary>
    /// <param name="WndProc">消息,true不拦截回调</param>
    public void WndProc(Func<Message, bool> WndProc)
    {
        _WndProcAction = WndProc;
    }

#line hidden
    // 此处会不断的执行消息循环
    protected override void WndProc(ref Message msg)
    {
        if (_WndProcAction is null)
            return;
        if (_WndProcAction.Invoke(msg))
            base.WndProc(ref msg);
    }
#line default

    #region Dispose
    public bool IsDisposed = false;

    /// <summary>
    /// 手动调用释放
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// 析构函数调用释放
    /// </summary>
    ~NativeCallProc()
    {
        Dispose(false);
    }
   
    protected virtual void Dispose(bool disposing)
    {
        // 不重复释放,并设置已经释放
        if (IsDisposed) return;
        IsDisposed = true;

        // 释放占用窗体的句柄
        ReleaseHandle();
    }
    #endregion
}

调用

using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
public class Anchor {
    //子类化 声明一定要放这里哦,不然GC会释放掉哦!
    //Acad主窗口的句柄   
    public static NativeCallProc _AcadCallWProc = new(Acap.MainWindow.Handle);

    public Anchor()
    {
        //57600_new || 18_qnew 对应cad命令
        IntPtr _new = (IntPtr)57600;
        IntPtr _qnew = (IntPtr)18;
        IntPtr _chongZhi = (IntPtr)0x0001e100;//重置之后ctrl+n取得的

        _AcadCallWProc.WndProc(m => {
            try
            {
                var msg = (WM)m.Msg;
                if (msg == WM.WM_COMMAND)
                {
                    //拦截原生的发送方式,因其(发送消息)导致致命错误,调用我自己写的新建文档函数
                    //WinApi.SendMessage(Acap.MainWindow.Handle, (int)MsgType.WM_COMMAND, (IntPtr)18, IntPtr.Zero);//qnew
                    //WinApi.SendMessage(Acap.MainWindow.Handle, (int)MsgType.WM_COMMAND, (IntPtr)57600, IntPtr.Zero);//new
                    if (m.WParam == _new || m.WParam == _qnew || m.WParam == _chongZhi)
                    {
                        this._DocWindow.Qnew();
                        // Qnew见
                        // https://gitee.com/inspirefunction/CadLabelBar/blob/master/src/LabelBarStandard/View/DocWindow.xaml.cs
                        // 之后是阻止cad接收win32的ctrl+n消息,需要键盘钩子hook
                        return false;//不回调,禁止了命令
                    }
                }
                AutoDocs.SyncForm?.Loaded(); //为什么有这句?见 https://www.cnblogs.com/JJBox/p/14179921.html
            }
            catch //cad崩溃的时候会触发
            {
                Debugger.Break();
            }
            return true;//可回调的都进入这里
        });
    }
}

注册表修补

但是截获了又该如何处理呢?

从"重置配置"得知,cad将当前配置放在注册表中,
而重置之后不关闭cad直接去获取注册表,是一份残缺的注册表.
而重置之后重启cad,
cad会自动补充完成这份注册表,是一份完整的注册表.

对比两份注册表就可以得知需要添加一些什么注册表到内部..
大概率就是COM的UUID信息写入出错.
如果你有兴趣一个个尝试删除,
你会得到某个注册表才是必须的,我懒得实验了,直接修复了全部.

public static class AcadRegistry {
    public static void CreateDocumentsComplementation() {
        bool writable = true;
        // 当前用户配置的名称
        var cprofile = CadSystem.Getvar("cprofile");

#if AC2006 || AC2007 || AC2008 || AC2009 || AC20081 || AC2011 || AC2012
        string key = HostApplicationServices.Current.RegistryProductRootKey; //这里浩辰读出来是""
#elif !HC2020
        string key = HostApplicationServices.Current.UserRegistryProductRootKey;
#endif
        using var currentUser = Registry.CurrentUser.OpenSubKey(key, writable);

        // 注册表_当前配置
        using var regedit_Cprofile = currentUser.CreateSubKey($"Profiles\\{cprofile}");

        // 注册表_
        using var regedit_Dialogs = regedit_Cprofile.CreateSubKey("Dialogs");
        using var r1 = regedit_Dialogs.CreateSubKey("AcDim:DimstyleFormat");
        var vns = r1.GetValueNames();
        SetReg(vns, "ActiveTab", 0, RegistryValueKind.DWord);

        using r2 = regedit_Dialogs.CreateSubKey("AllAnavDialogs");
        vns = r2.GetValueNames();
        SetReg(vns, "PlacesOrder0", "History");
        SetReg(vns, "PlacesOrder0Display", "历史记录");
        SetReg(vns, "PlacesOrder0Ext", string.Empty);

        SetReg(vns, "PlacesOrder1", "Personal");
        SetReg(vns, "PlacesOrder1Display", "文档");
        SetReg(vns, "PlacesOrder1Ext", string.Empty);

        SetReg(vns, "PlacesOrder2", "Favorites");
        SetReg(vns, "PlacesOrder2Display", "收藏夹");
        SetReg(vns, "PlacesOrder2Ext", string.Empty);

        SetReg(vns, "PlacesOrder3", "FTPSites");
        SetReg(vns, "PlacesOrder3Display", "FTP");
        SetReg(vns, "PlacesOrder3Ext", string.Empty);

        SetReg(vns, "PlacesOrder4", "Desktop");
        SetReg(vns, "PlacesOrder4Display", "桌面");
        SetReg(vns, "PlacesOrder4Ext", string.Empty);

        SetReg(vns, "PlacesOrder5", "ACPROJECT");
        SetReg(vns, "PlacesOrder5Display", "Buzzsaw");
        SetReg(vns, "PlacesOrder5Ext", string.Empty);

        SetReg(vns, "PlacesOrder6", string.Empty);

        string[] cus = { "CustomizeDialog", "DrawingSettingsDialog", "OptionsDialog" };
        foreach (var item in cus) {
            using r3 = regedit_Dialogs.CreateSubKey(item + "\\TabExtensions");
            vns = r3.GetValueNames();
            SetReg(vns, "acmgd.dll", "acmgd.dll");
        }

        using var r4 = regedit_Dialogs.CreateSubKey("选择样板");
        vns = r4.GetValueNames(); 
        var templatePath = CadSystem.Getvar("TemplatePath");//模板路径
        SetReg(vns, "InitialFilterIndex", 0x00000000, RegistryValueKind.DWord);
        SetReg(vns, "ViewMode", 0x00000004, RegistryValueKind.DWord);

        SetReg(vns, "InitialDirectory", templatePath + "\\", RegistryValueKind.String);

        SetReg(vns, "PreviewVisible", 0x00000001, RegistryValueKind.DWord);
        SetReg(vns, "X", 0x00000226, RegistryValueKind.DWord);
        SetReg(vns, "Y", 0x00000158, RegistryValueKind.DWord);

        SetReg(vns, "Width", 0x00000283, RegistryValueKind.DWord);
        SetReg(vns, "Height", 0x000001a1, RegistryValueKind.DWord);


        //注册表_Dialogs_Window
        using var r5 = regedit_Cprofile.CreateSubKey("Drawing Window");
        vns = r5.GetValueNames();
        SetReg(vns, "SDIMode", 0, RegistryValueKind.DWord);

        //注册表_Editor_Configuration
        using var r6 = regedit_Cprofile.CreateSubKey("Editor Configuration");
        vns = r6.GetValueNames();
        //"CustomDictionary" = "C:\\Users\\LQH\\AppData\\Roaming\\Autodesk\\AutoCAD 2008\\R17.1\\chs\\support\\sample.cus"
        // 这里没有设置好
        SetReg(vns, "CustomDictionary", "", RegistryValueKind.String);
        SetReg(vns, "MainDictionary", "enu", RegistryValueKind.String);
        SetReg(vns, "MTextEditor", "内部", RegistryValueKind.String);
        var temp = Path.GetTempPath();
        SetReg(vns, "SaveFilePath", temp, RegistryValueKind.String);
        SetReg(vns, "XrefLoadPath", temp, RegistryValueKind.String);


        //注册表_General
        using var r7 = regedit_Cprofile.CreateSubKey("General");
        vns = r7.GetValueNames();
        SetReg(vns, "ACET-ACETMAIN-MENULOADED", "1", RegistryValueKind.String);
        SetReg(vns, "Anyport", 0, RegistryValueKind.DWord);
        SetReg(vns, "Attdia", 0, RegistryValueKind.DWord);
        SetReg(vns, "Attreq", 1, RegistryValueKind.DWord);
        SetReg(vns, "Blipmode", 0, RegistryValueKind.DWord);
        SetReg(vns, "Coords", 1, RegistryValueKind.DWord);
        //  SetReg(vns, "Delobj", 0xffffffff, RegistryValueKind.DWord);//这句会报错,不设置也没事        
        SetReg(vns, "Dragmode", 2, RegistryValueKind.DWord);
        SetReg(vns, "HideSystemPrinters", 0, RegistryValueKind.DWord);

        SetReg(vns, "LayerPMode", "1", RegistryValueKind.String);
        SetReg(vns, "MRUConfig", "Adobe PDF", RegistryValueKind.String);

        SetReg(vns, "OLEQUALITY", 3, RegistryValueKind.DWord);
        SetReg(vns, "Osmode", 0x00001025, RegistryValueKind.DWord);
        SetReg(vns, "PAPERUPDATE", 0, RegistryValueKind.DWord);
        SetReg(vns, "Pickstyle", 1, RegistryValueKind.DWord);
        SetReg(vns, "PLOTLEGACY", 0, RegistryValueKind.DWord);
        SetReg(vns, "PLSPOOLALERT", 0, RegistryValueKind.DWord);
        SetReg(vns, "PSTYLEPOLICY", 1, RegistryValueKind.DWord);
        SetReg(vns, "RASTERTHRESHOLD", 0x14, RegistryValueKind.DWord);
        SetReg(vns, "RASTERPERCENT", 0x14, RegistryValueKind.DWord);
        SetReg(vns, "UseMRUConfig", 0, RegistryValueKind.DWord);
        SetReg(vns, "Validation Policy", 0x3, RegistryValueKind.DWord);
        SetReg(vns, "Validation Strategy", 0x1, RegistryValueKind.DWord);


        //注册表_
        using var r8 = regedit_Cprofile.CreateSubKey("MLeader");
        vns = r8.GetValueNames();
        SetReg(vns, "", "", RegistryValueKind.String);//这种是默认吗???
        SetReg(vns, "CreatedMode", 1, RegistryValueKind.DWord);

        //注册表_
        using var regedit_pps = regedit_Cprofile.CreateSubKey("Previous plot settings");
        using var r9 = regedit_pps.CreateSubKey("Layout");
        using var r10 = regedit_pps.CreateSubKey("Model");

        //注册表_
        using var regedit_StatusBar = regedit_Cprofile.CreateSubKey("StatusBar");
        var names = new string[] { "AnnotationScales", "AnnotationVisibility", "AutoScale",
            "CleanScreenPane", "CursorCoordinatesPane",
            "DynamicUCSPane","DynInputPane",
            "FloatPane", "GridPane",
            "LayoutIconPane", "LayoutMoreIconPane","LineweightPane",
            "ModelIconPane",
            "OrthoPane","OSnapPane","OTrackPane",
            "Paper/ModelPane","PolarPane",
            "SnapPane","SpacerPane",
            "TabletPane",
            "ViewportLockState","ViewportScales",
            "VpMaxPrevPane","VpMaxPane","VpMaxNextPane"
        };
        using r11 = regedit_StatusBar.CreateSubKey("Application");
        vns = r11.GetValueNames();
        SetReg(vns, names, 1, RegistryValueKind.DWord);
    }

    // 如果注册表没有这个值才加入
    static void SetReg(string[] regeditValueNames,
        string name, object value, RegistryValueKind kind = RegistryValueKind.String) {
        if (regeditValueNames.Contains(name) return;
        regedit.SetValue(name, value, kind);
    }
    static void SetReg(string[] regeditValueNames,
        string[] names, object value, RegistryValueKind kind) {
        for(int i =0; i< names.Length; i++){
            if (regeditValueNames.Contains(names[i]) return;
            regedit.SetValue(names[i], value, kind);
        }
    }

}

这样创建cad文档就没有了致命错误了...开心~
若其他cad也存在这样的情况,那么如上设置就可以了.
(完)

posted @ 2020-12-25 00:29  惊惊  阅读(962)  评论(0编辑  收藏  举报