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)--截获了这个错误,
非常的干脆利落得到.
子类化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也存在这样的情况,那么如上设置就可以了.
(完)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)