cad.net 子类化拦截消息循环

说明

在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当前文档,
此时无法利用doc.Editor发送新建图纸命令.

方案三:Win32API发送消息

e大协助了我利用了win32API的方式来处理,
但是此方式在Acad上面只能用于传送给当前进程,
不能跨进程,可能是因为Acad.exe进程有数字签名,
若需要跨进程,需要改用Socket/管道通讯/共享内存.

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

子类化拦截

实现方案三的时候,之前已经实现过子类化窗口来拦截cad的消息,
突然间拦截了一个错误:
Autodesk.AutoCAD.Runtime.Exception:“eFilerError”

步骤就是:
重置配置(op)--创建cad文档(ctrl+n)--截获了这个错误,

非常的干脆利落得到.
img

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

若要阻止Acad接收win32的ctrl+n消息,用键盘钩子.
Qnew如何构造的:
https://gitee.com/inspirefunction/CadLabelBar/tree/dev/src/LabelBar/View/DocWindow.xaml.cs

https://www.cnblogs.com/JJBox/p/14179921.html

发送Win32到cad消息例子:
https://www.cnblogs.com/JJBox/p/13766772.html

WPF方案

代替子类化窗口过程(这个方案为什么不行呢)

using System;
using System.Runtime.InteropServices;
using System.Windows.Threading;
using System.Windows.Interop;

namespace JoinBox;
public class Anchor : IDisposable {
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern uint RegisterWindowMessage(string lpString);
    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();
    [DllImport("user32.dll")]
    private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewValue);
    [DllImport("user32.dll")]
    private static extern IntPtr CallWindowProc(IntPtr lpfnWndProc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
    // 窗口过程委托
    private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    private const int WH_CALLWNDPROC = 0x0014;
    private HwndSource _source;
    private Dispatcher _dispatcher;
    public static uint WM_OPEN_TASK;
    private IntPtr _originalWndProc;

    public static Anchor? Create() {
        // 后台为IntPtr.Zero
        var hwnd = GetForegroundWindow();
        if (hwnd == IntPtr.Zero) return null;
        hwnd = Acap.MainWindow.Handle;
        if (hwnd == IntPtr.Zero) return null;
        var dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
        if (dispatcher is null) return null;
        var source = HwndSource.FromHwnd(hwnd);
        if (source is null) return null; 
        return new Anchor(hwnd, dispatcher);
    }

    public Anchor(IntPtr hwnd, Dispatcher dispatcher) {
        _dispatcher = dispatcher;
        _source = HwndSource.FromHwnd(hwnd);
        WM_OPEN_TASK = RegisterWindowMessage("WM_OPEN_TASK");

/*
        // 设置窗口过程的win32方式
        _originalWndProc = SetWindowLongPtr(hwnd, WH_CALLWNDPROC, 
            Marshal.GetFunctionPointerForDelegate(
                new WndProcDelegate(this.WndProc)));
*/
        // 钩子和窗口过程冲突
        _source.AddHook(new HwndSourceHook(this.WndProc));
    }

    private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
        if (msg == WM_OPEN_TASK) {
            // 异步处理任务
            string path = Marshal.PtrToStringUni(lParam);
            Task.Run(() => HandleOpenTask(path));
            // 回调UI线程
            _dispatcher.InvokeAsync(OnTaskCompleted);
            handled = true; // 已经处理
            return IntPtr.Zero;
        }
        return IntPtr.Zero;
    }

    protected virtual void Dispose(bool disposing) {
       if (disposing) {
            // 移除窗口过程
            // SetWindowLongPtr(_source.Handle, WH_CALLWNDPROC, _originalWndProc);
            // 钩子和窗口过程冲突
            _source.RemoveHook(new HwndSourceHook(this.WndProc));
            _source.Dispose();
            WM_OPEN_TASK = 0;
            _originalWndProc = IntPtr.Zero;
        }
    }
    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    ~Anchor() => Dispose(false);

    private void OnTaskCompleted() {
        // UI线程回调逻辑(如更新控件)
        // statusLabel.Content = "任务完成";
    }

    // 处理自定义命令
    private void HandleOpenTask(string path) {
        if (File.Exists(path)) {
            var dm = Acap.DocumentManager;
            var doc = dm.Open(path, false);
        }
    }
}

子类化

全系列的acad能用这个方案.

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using System.Diagnostics;
using SynchronizationContext = System.Threading.SynchronizationContext;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;

namespace JoinBox;
    public delegate bool MessageFunc(ref Message message);

/// <summary>
/// 窗口控件子类化
/// </summary>
public class NativeCallProc : NativeWindow, IDisposable {
    private readonly IntPtr _windowHandle;
    private readonly SynchronizationContext _syncContext;
    private MessageFunc? _messageHandler = null;

    public static NativeCallProc? Create(IntPtr windowHandle) {
        if (windowHandle == IntPtr.Zero) return null;
        var syncContext = SynchronizationContext.Current;
        if (syncContext is null) return null;
        return new NativeCallProc(windowHandle, syncContext);
    }

    private NativeCallProc(IntPtr windowHandle, SynchronizationContext syncContext) {
        if (windowHandle == IntPtr.Zero)
            throw new ArgumentNullException(nameof(windowHandle), "窗口句柄不能为空");
        if (syncContext is null)
            throw new ArgumentNullException(nameof(syncContext), "当前线程没有关联的同步上下文");
        _windowHandle = windowHandle;
        _syncContext = syncContext;
        base.AssignHandle(windowHandle);
    }

    public void WndProc(MessageFunc handler) {
        _messageHandler = handler;
    }

    [System.Diagnostics.DebuggerNonUserCode]
    protected override void WndProc(ref Message msg) {
        if (_isDisposed || _messageHandler is null)
            return;
        try {
            if (_messageHandler.Invoke(ref msg))
                base.WndProc(ref msg);
        } catch (Exception ex) {
            Trace.WriteLine(nameof(NativeCallProc), $"窗口消息处理异常: {ex}");
            Debugger.Break();
        }
    }

    public void Send(Action ac) {
        if (_isDisposed) return;
        _syncContext.Send(state => ac.Invoke(), null);
    }
    public void Post(Action ac) {
        if (_isDisposed) return;
        _syncContext.Post(state => ac.Invoke(), null);
    }

    #region IDisposable Implementation
    private bool _isDisposed;
    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing) {
        if (_isDisposed) return;
        _isDisposed = true;
        // 确保在消息循环中不再处理新消息
        _messageHandler = null;
        // 释放窗口句柄
        base.ReleaseHandle();
    }
    #endregion
}

public class Anchor {
    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();

    NativeCallProc _acadCallWProc;
    public NativeCallProc AcadCallWProc => _acadCallWProc;

    // 自定义唯一消息编号
    public static uint WM_OPEN_TASK;
    // Acad的命令标记
    public const int WM_COMMAND = 0x0111;

    // 子类化拦截Acad主窗口句柄,释放会在进程结束前.
    public static Anchor? Create() {
        // 后台为IntPtr.Zero
        var hwnd = GetForegroundWindow();
        if (hwnd == IntPtr.Zero) return null;
        hwnd = Acap.MainWindow.Handle;
        var ncp = NativeCallProc.Create(hwnd);
        if (ncp is null) return null;
        return new Anchor(ncp);
    }

    // 同进程可以收到消息和LParam,
    // 但是跨进程收不到LParam,需要改用 共享内存/管道通讯/socket.
    // https://blog.csdn.net/wlslblue/article/details/38613871
    // 原因是UIPI(用户界面特权隔离)
    // ChangeWindowMessageFilter 和 ChangeWindowMessageFilterEx
    //
    private Anchor(NativeCallProc ncp) {
        WM_OPEN_TASK = RegisterWindowMessage("WM_OPEN_TASK");
        _acadCallWProc = npc;
        _acadCallWProc.WndProc(ref m => {
            if (m.Msg == WM_COMMAND) {
                return HandleCommand(ref m);
            }
            if (m.Msg == WM_OPEN_TASK) {
                string path = Marshal.PtrToStringUni(m.LParam);
                MessageBox.Show($"收到消息: {path}");
                if (!File.Exists(path)) return true;
                _acadCallWProc.Post(() => HandleOpenTask(path));
                return false;
            }
            return true;
        });
    }

    // 全部全部文档就没有命令栏,要打开文档需要发送消息,
    // 如果是其他cad命令,则需要发送异步命令加入命令队
    // 处理打开命令
    private bool HandleCommand(ref Message m) {
        IntPtr _new = (IntPtr)57600;
        IntPtr _qnew = (IntPtr)18;
        // 重置后使用ctrl+n会取得的这个值
        IntPtr _resetQnew = (IntPtr)0x0001e100;
        if (m.WParam == _new || m.WParam == _qnew || m.WParam == _resetQnew) {
            // this._DocWindow.Qnew();
            return false;
        }
        return true;
    }

    // 处理自定义命令
    private void HandleOpenTask(string path) {
        var dm = Acap.DocumentManager;
        var doc = dm.Open(path, false);
    }
}

测试命令

// 放到自动运行的接口上面使用,此处采用IFox的
public class AnchorCommands {
    static Anchor _anchor;
    [IFoxInitialize]
    public void TestAnchor() {
        Acap.Idle += (s, e) => {
            _anchor ??= Anchor.Create();
        };
    }

    [CommandMethod(nameof(AllOpen))]
    public void AllOpen() {
        if (_anchor is null) 
            throw new(nameof(_anchor));

        var thisCadHandle = Acap.MainWindow.Handle;
        var thisCadExePath = Process.GetCurrentProcess()
            .MainModule.FileName;

        var prs = Process.GetProcessesByName("acad")
            .Where(pr => pr.MainModule != null &&
                pr.MainWindowHandle != IntPtr.Zero);

// 调试期间允许发送给自己
#if RELEASE
        prs = prs.Where(pr => pr.MainWindowTitle != string.Empty &&
                pr.MainWindowHandle != thisCadHandle &&
                !string.Equals(pr.MainModule.FileName,
                    thisCadExePath,
                    StringComparison.CurrentCultureIgnoreCase));
#endif

        var handles = prs
            .Select(pr => pr.MainWindowHandle)
            .ToArray();

        // 发送给非自己的Acad打开这张图纸
        foreach(var handle in handles)
            WinApi.SendAcadProcesses(handle, "C:\\图纸.dwg");
    }

    /// <summary>
    /// 发送信息到进程
    /// </summary>
    /// <param name="hwnd">主窗口的句柄</param>
    /// <param name="toSend">报文</param>
    public void SendAcadProcesses(IntPtr hwnd, string filePath) {
        if (!File.Exists(filePath)) {
            Trace.WriteLine(nameof(SendAcadProcesses), $"文件路径不存在: {filePath}");
            return;
        }
        // 将文件路径转换为com内存
        IntPtr lParam = Marshal.StringToCoTaskMemUni(filePath);
        // 发送自定义消息
        WinApi.SendMessage(hwnd, Anchor.WM_OPEN_TASK, IntPtr.Zero, lParam);
        // 发送方等待同步消息结束并释放内存
        Marshal.FreeCoTaskMem(lParam);
    }
}

非COM内存方案

方案一:使用全局内存(跨进程不行)

发送方
由于是同步消息,
所以会等待 接收方 接收之后再处理.
因此回收内存由发送方负责.

namespace Win32API;
public partial class WinApi {
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr hWnd, uint wMsg, IntPtr wParam, ref CopyDataStruct lParam);
    // 此分配内存的函数非常底层,还可以不清零,例如构造一亿个结构体对象
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr GlobalAlloc(uint uFlags, UIntPtr dwBytes);
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool GlobalFree(IntPtr hMem);

    public const uint GMEM_MOVEABLE = 0x0002;
    public const uint GMEM_ZEROINIT = 0x0040;
    public const uint WM_COPYDATA = 0x004A;

    [StructLayout(LayoutKind.Sequential)]
    public struct CopyDataStruct {
        public IntPtr dwData;
        public uint cbData;
        public IntPtr lpData;
    }
}
using System;
using System.Runtime.InteropServices;
using System.Text;

public class AnchorCommands {
    public static void SendAcadProcesses(IntPtr hwnd, string text) {
        // 将字符串转换字节数组
        byte[] data = Encoding.Unicode.GetBytes(text);
        // 申请字符串全局内存
        IntPtr hMem = WinApi.GlobalAlloc(WinApi.GMEM_MOVEABLE | WinApi.GMEM_ZEROINIT,
            (UIntPtr)data.Length);
        if (hMem == IntPtr.Zero)
            throw new Exception("全局内存申请失败");

        try {
            // 拷贝字节数组到全局内存
            Marshal.Copy(data, 0, hMem, data.Length);

            // 申请结构体全局内存
            IntPtr hStruct = WinApi.GlobalAlloc(WinApi.GMEM_MOVEABLE | WinApi.GMEM_ZEROINIT, 
                (UIntPtr)Marshal.SizeOf(typeof(WinApi.CopyDataStruct)));
            if (hStruct == IntPtr.Zero) 
                throw new Exception("结构体内存申请失败");

            // 填充结构体
            var cds = new WinApi.CopyDataStruct {
                dwData = (IntPtr)1,
                cbData = (uint)data.Length,
                lpData = hMem
            };
            // 拷贝结构体到全局内存
            Marshal.StructureToPtr(cds, hStruct, false);
            // 发送同步消息
            WinApi.SendMessage(hwnd, WinApi.WM_COPYDATA, IntPtr.Zero, hStruct);
        } finally {
            // 发送方等待同步消息结束并释放内存
            if (hStruct != IntPtr.Zero) {
                bool result = GlobalFree(hStruct);
                if (!result) Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
            }
            if (hMem != IntPtr.Zero) {
                bool result = GlobalFree(hMem);
                if (!result) Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
            }
        }
    }
}

接收方:

 Anchor 构造函数:
           if (m.Msg == WinApi.WM_COPYDATA) {
               var cds = (WinApi.CopyDataStruct)m.GetLParam(typeof(WinApi.CopyDataStruct));
               if (cds.cbData == 0) return true;
               // 直接读取共享内存
               byte[] buffer = new byte[cds.cbData];
               // 运行到下面这句,崩溃
               Marshal.Copy(cds.lpData, buffer, 0, cds.cbData);
               string path = Encoding.Unicode.GetString(buffer);
               MessageBox.Show($"收到消息: {path}");
               if (!File.Exists(path)) return true;
               _acadCallWProc.Post(() => HandleOpenTask(path));
               return false;
            }

方案二,发送消息阻止和允许(跨进程不行)

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

public static class WinApi {
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr SendMessage(
        IntPtr hWnd,
        uint Msg,
        IntPtr wParam,
        ref COPYDATASTRUCT lParam
    );

    [StructLayout(LayoutKind.Sequential)]
    public struct COPYDATASTRUCT
    {
        public uint dwData;
        public IntPtr lpData;
        public uint cbData;
    }

    public static string GetString(IntPtr lParam) {
        var cds = Marshal.PtrToStructure<COPYDATASTRUCT>(lParam);
        byte[] receivedBytes = new byte[cds.cbData];
        Marshal.Copy(cds.lpData, receivedBytes, 0, receivedBytes.Length);
        string receivedData = Encoding.UTF8.GetString(receivedBytes);
        return receivedData.TrimEnd('\0');
    }

    public static IntPtr SendString(IntPtr targetHwnd, string message, IntPtr dwData = default)
    {
        if (string.IsNullOrEmpty(message))
        {
            MessageBox.Show("消息内容不能为空");
            return IntPtr.Zero;
        }
        byte[] messageBytes = Encoding.UTF8.GetBytes(message + "\0"); // 包括终止符
        IntPtr unmanagedMemory = Marshal.AllocCoTaskMem(messageBytes.Length);
        if (unmanagedMemory == IntPtr.Zero)
        {
            MessageBox.Show("内存申请失败");
            return IntPtr.Zero;
        }
        try
        {
            // 将字符串复制到非托管内存
            Marshal.Copy(messageBytes, 0, unmanagedMemory, messageBytes.Length);
            var cds = new COPYDATASTRUCT
            {
                dwData = dwData, // 数据标识
                lpData = unmanagedMemory,
                cbData = (uint)messageBytes.Length // 字节长度
            };
            return SendMessage(targetHwnd, WM_COPYDATA, IntPtr.Zero, ref cds);
        }
        finally
        {
            // 释放非托管内存
            Marshal.FreeCoTaskMem(unmanagedMemory);
        }
    }
}

// https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-changewindowmessagefilterex
public class ChangeWindowMessage {
    // 窗口权限
    [Flags]
    public enum Status : uint {
        RESET = 0,
        ALLOW = 1, // 允许通过
        DISALLOW = 2
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct FilterStruct {
        public uint CbSize;
        public Status ExtStatus;
    }

    [DllImport("user32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd, 
       uint message, Status action, in FilterStruct fs);

    /// <summary>
    /// 允许WPF窗口接收消息
    /// </summary>
    /// <param name="window"></param>
    public static bool AllowMessage(Window window) {
        var status = new FilterStruct() { CbSize = 8 };
        return ChangeWindowMessageFilterEx(
            new WindowInteropHelper(window).Handle,
            WinApi.WM_COPYDATA, Status.ALLOW, in status);
    }

    /// <summary>
    /// 允许句柄窗口接收消息
    /// </summary>
    /// <param name="handle"></param>
    public static bool AllowMessage(IntPrt handle) {
        var status = new FilterStruct() { CbSize = 8 };
        return ChangeWindowMessageFilterEx(handle,
            WinApi.WM_COPYDATA, Status.ALLOW, in status);
    }

}

接收端

public partial class ReceiverForm : Form
{
    public ReceiverForm()
    {
        InitializeComponent();
        Idle += (s, e) = > {
            Filter(this.Handle);
        };
    }

    SynchronizationContext? _syncContext = null;

    private void Filter(IntPtr handle)
    {
        // 等到窗口过程上下文出现
        if (_syncContext is not null) return;
        _syncContext ??= SynchronizationContext.Current;
        if (_syncContext is null) return;
        // 允许接收端窗口接收消息
        bool result = ChangeWindowMessage.AllowMessage(handle);
        if (!result)
            MessageBox.Show($"错误代码: {Marshal.GetLastWin32Error()}");
    }

    protected override void WndProc(ref Message m) {
        if (m.Msg == WinApi.WM_COPYDATA) {
            string receivedData = WinApi.GetString(m.LParam);
            MessageBox.Show($"收到消息: {receivedData}");
        }
        base.WndProc(ref m);
    }
}

发送端

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

public partial class SenderForm : Form
{
    public SenderForm()
    {
        InitializeComponent();
    }

    private void btnSend_Click(object sender, EventArgs e)
    {
        // 替换为实际的接收端窗口标题
        IntPtr targetHwnd = WinApi.FindWindow(null, "Receiver Application");
        if (targetHwnd == IntPtr.Zero) {
            MessageBox.Show("未找到目标窗口");
            return;
        }
        string message = "Hello from Sender!";
        IntPtr result = WinApi.SendString(targetHwnd, message);
        if (result != IntPtr.Zero)
            MessageBox.Show("消息发送成功!");
        else
            MessageBox.Show($"错误代码: {Marshal.GetLastWin32Error()}");
        }
    }
}  

方案三

我们实验了一个可行的方案是:
1,实验直接在Acad开一个winform,然后获取消息过程.
2,通过另一个进程SendMessage过来.
3,此时winform能够收到消息的.
这代表了
不是数组签名进程阻挡,
而是主窗口的消息过程钩子比较底层,能够过滤掉我们钩子.

不过它实现比较复杂,还不如共享内存方便.

注册表修补

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

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

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

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

#if NET35
        // 浩辰读出来是"",需要手写
        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 @   惊惊  阅读(1084)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示