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";//设置默认选中
}
var sa = new Autodesk.AutoCAD.Windows.OpenFileDialog("选择模板", lastTemplate, "dwt", "",
OpenFileDialogFlags.AllowAnyExtension | OpenFileDialogFlags.ForceDefaultFolder);
if (sa.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
//System.Runtime.InteropServices.COMException:“内部应用程序出错。”
Acap.DocumentManager.Add(sa.Filename);
}
方案二:发送命令
然后我分析了一下,用 Acap.DocumentManager.Add() 创建文档是愚蠢的(后来证明并不...)
正常也应该直接调用cad自带的方式,毕竟官方的方法可以避免错误...
于是乎,我改用了
Acap.DocumentManager.MdiActiveDocument.SendStringToExecute("_new\n", false, true, true);
//第二个参数true时,命令必须有CommandFlags.Session,否则容易致命错误
方案三:Win32API发送消息
但是遇到一个尴尬的事情,没有文档的时候不存在 MdiActiveDocument 此时发送新建命令是错误的...
然后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”
而整个步骤就是: 重置cad--创建cad文档(ctrl+n)--截获了这个错误,非常的干脆利落得到.
封装
子类化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会自动补充完成这份注册表,是一份完整的注册表.
对比两份注册表就可以得知需要添加一些什么注册表到内部..
如果你有兴趣一个个尝试删除,你会得到某个注册表才是必须的,我懒得实验了,直接修复了全部...
public static class AcadRegistry
{
/// <summary>
/// Acad08重置之后,新建文档会致命错误.
/// 重置cad会清理注册表,这个时候新建图纸就会致命错误(即使是自带的ctrl+n操作)
/// 所以这个时候导出-用户配置注册表-是缺失的.
/// 重启cad之后,会自动补充这部分注册表,这个时候是完整的(另存为图纸也可以实现,就是完全关闭掉所有文档则没法另存).
/// 两份注册表就可以用来对比.
/// </summary>
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
var currentUser = Registry.CurrentUser.OpenSubKey(key, writable);
//注册表_当前配置
var regedit_Cprofile = currentUser.CreateSubKey($"Profiles\\{cprofile}");
//注册表_
var regedit_Dialogs = regedit_Cprofile.CreateSubKey("Dialogs");
{
var acDim_DimstyleFormat = regedit_Dialogs.CreateSubKey("AcDim:DimstyleFormat");
{
ChangReg(acDim_DimstyleFormat, "ActiveTab", 0, RegistryValueKind.DWord);
}
var allAnavDialogs = regedit_Dialogs.CreateSubKey("AllAnavDialogs");
{
ChangReg(allAnavDialogs, "PlacesOrder0", "History");
ChangReg(allAnavDialogs, "PlacesOrder0Display", "历史记录");
ChangReg(allAnavDialogs, "PlacesOrder0Ext", string.Empty);
ChangReg(allAnavDialogs, "PlacesOrder1", "Personal");
ChangReg(allAnavDialogs, "PlacesOrder1Display", "文档");
ChangReg(allAnavDialogs, "PlacesOrder1Ext", string.Empty);
ChangReg(allAnavDialogs, "PlacesOrder2", "Favorites");
ChangReg(allAnavDialogs, "PlacesOrder2Display", "收藏夹");
ChangReg(allAnavDialogs, "PlacesOrder2Ext", string.Empty);
ChangReg(allAnavDialogs, "PlacesOrder3", "FTPSites");
ChangReg(allAnavDialogs, "PlacesOrder3Display", "FTP");
ChangReg(allAnavDialogs, "PlacesOrder3Ext", string.Empty);
ChangReg(allAnavDialogs, "PlacesOrder4", "Desktop");
ChangReg(allAnavDialogs, "PlacesOrder4Display", "桌面");
ChangReg(allAnavDialogs, "PlacesOrder4Ext", string.Empty);
ChangReg(allAnavDialogs, "PlacesOrder5", "ACPROJECT");
ChangReg(allAnavDialogs, "PlacesOrder5Display", "Buzzsaw");
ChangReg(allAnavDialogs, "PlacesOrder5Ext", string.Empty);
ChangReg(allAnavDialogs, "PlacesOrder6", string.Empty);
}
string[] cus = { "CustomizeDialog", "DrawingSettingsDialog", "OptionsDialog" };
foreach (var item in cus)
{
var cu = regedit_Dialogs.CreateSubKey(item + "\\TabExtensions");
ChangReg(cu, "acmgd.dll", "acmgd.dll");
}
var xzyb = regedit_Dialogs.CreateSubKey("选择样板");
{
var templatePath = CadSystem.Getvar("TemplatePath");//模板路径
ChangReg(xzyb, "InitialFilterIndex", 0x00000000, RegistryValueKind.DWord);
ChangReg(xzyb, "ViewMode", 0x00000004, RegistryValueKind.DWord);
ChangReg(xzyb, "InitialDirectory", templatePath + "\\", RegistryValueKind.String);
ChangReg(xzyb, "PreviewVisible", 0x00000001, RegistryValueKind.DWord);
ChangReg(xzyb, "X", 0x00000226, RegistryValueKind.DWord);
ChangReg(xzyb, "Y", 0x00000158, RegistryValueKind.DWord);
ChangReg(xzyb, "Width", 0x00000283, RegistryValueKind.DWord);
ChangReg(xzyb, "Height", 0x000001a1, RegistryValueKind.DWord);
}
}
//注册表_Dialogs_Window
var regedit_Drawing_Window = regedit_Cprofile.CreateSubKey("Drawing Window");
{
ChangReg(regedit_Drawing_Window, "SDIMode", 0, RegistryValueKind.DWord);
}
//注册表_Editor_Configuration
var regedit_Editor_Configuration = regedit_Cprofile.CreateSubKey("Editor Configuration");
{
//"CustomDictionary" = "C:\\Users\\LQH\\AppData\\Roaming\\Autodesk\\AutoCAD 2008\\R17.1\\chs\\support\\sample.cus"
//这里没有设置好
ChangReg(regedit_Editor_Configuration, "CustomDictionary", "", RegistryValueKind.String);
ChangReg(regedit_Editor_Configuration, "MainDictionary", "enu", RegistryValueKind.String);
ChangReg(regedit_Editor_Configuration, "MTextEditor", "内部", RegistryValueKind.String);
var temp = Path.GetTempPath();
ChangReg(regedit_Editor_Configuration, "SaveFilePath", temp, RegistryValueKind.String);
ChangReg(regedit_Editor_Configuration, "XrefLoadPath", temp, RegistryValueKind.String);
}
//注册表_General
var regedit_General = regedit_Cprofile.CreateSubKey("General");
{
ChangReg(regedit_General, "ACET-ACETMAIN-MENULOADED", "1", RegistryValueKind.String);
ChangReg(regedit_General, "Anyport", 0, RegistryValueKind.DWord);
ChangReg(regedit_General, "Attdia", 0, RegistryValueKind.DWord);
ChangReg(regedit_General, "Attreq", 1, RegistryValueKind.DWord);
ChangReg(regedit_General, "Blipmode", 0, RegistryValueKind.DWord);
ChangReg(regedit_General, "Coords", 1, RegistryValueKind.DWord);
//try
//{
// ChangReg(regedit_General, "Delobj", 0xffffffff, RegistryValueKind.DWord);//这句会报错,不设置也没事
//}
//catch (Exception e)
//{
// throw e;
//}
ChangReg(regedit_General, "Dragmode", 2, RegistryValueKind.DWord);
ChangReg(regedit_General, "HideSystemPrinters", 0, RegistryValueKind.DWord);
ChangReg(regedit_General, "LayerPMode", "1", RegistryValueKind.String);
ChangReg(regedit_General, "MRUConfig", "Adobe PDF", RegistryValueKind.String);
ChangReg(regedit_General, "OLEQUALITY", 3, RegistryValueKind.DWord);
ChangReg(regedit_General, "Osmode", 0x00001025, RegistryValueKind.DWord);
ChangReg(regedit_General, "PAPERUPDATE", 0, RegistryValueKind.DWord);
ChangReg(regedit_General, "Pickstyle", 1, RegistryValueKind.DWord);
ChangReg(regedit_General, "PLOTLEGACY", 0, RegistryValueKind.DWord);
ChangReg(regedit_General, "PLSPOOLALERT", 0, RegistryValueKind.DWord);
ChangReg(regedit_General, "PSTYLEPOLICY", 1, RegistryValueKind.DWord);
ChangReg(regedit_General, "RASTERTHRESHOLD", 0x14, RegistryValueKind.DWord);
ChangReg(regedit_General, "RASTERPERCENT", 0x14, RegistryValueKind.DWord);
ChangReg(regedit_General, "UseMRUConfig", 0, RegistryValueKind.DWord);
ChangReg(regedit_General, "Validation Policy", 0x3, RegistryValueKind.DWord);
ChangReg(regedit_General, "Validation Strategy", 0x1, RegistryValueKind.DWord);
}
//注册表_
var regedit_MLeader = regedit_Cprofile.CreateSubKey("MLeader");
{
ChangReg(regedit_MLeader, "", "", RegistryValueKind.String);//这种是默认吗???
ChangReg(regedit_MLeader, "CreatedMode", 1, RegistryValueKind.DWord);
}
//注册表_
var regedit_Previous_plot_settings = regedit_Cprofile.CreateSubKey("Previous plot settings");
{
regedit_Previous_plot_settings.CreateSubKey("Layout");
regedit_Previous_plot_settings.CreateSubKey("Model");
}
//注册表_
var regedit_StatusBar = regedit_Cprofile.CreateSubKey("StatusBar");
{
var regedit_Application = regedit_StatusBar.CreateSubKey("Application");
ChangReg(regedit_Application, "AnnotationScales", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "AnnotationVisibility", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "AutoScale", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "CleanScreenPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "CursorCoordinatesPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "DynamicUCSPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "DynInputPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "FloatPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "GridPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "LayoutIconPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "LayoutMoreIconPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "LineweightPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "ModelIconPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "OrthoPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "OSnapPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "OTrackPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "Paper/ModelPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "PolarPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "SnapPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "SpacerPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "TabletPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "ViewportLockState", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "ViewportScales", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "VpMaxPrevPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "VpMaxPane", 1, RegistryValueKind.DWord);
ChangReg(regedit_Application, "VpMaxNextPane", 1, RegistryValueKind.DWord);
}
}
/// <summary>
/// 如果注册表没有这个值才加入,否则就不加入
/// </summary>
/// <param name="regedit"></param>
/// <param name="name"></param>
/// <param name="value"></param>
/// <param name="kind"></param>
private static void ChangReg(RegistryKey regedit, string name, object value,
RegistryValueKind kind = RegistryValueKind.String)
{
bool flag = true;
foreach (var item in regedit.GetValueNames())
if (item == name)
{
flag = false;
break;
}
if (flag)
regedit.SetValue(name, value, kind);
}
}
这样创建cad文档就没有了致命错误了...开心~
若其他cad也存在这样的情况,那么如上设置就可以了...
(完)