版本說明
本文測試為Acad2008(Cui)和Acad2021(Cuix)的案例,應該能夠涉及所有高低版本的操作.
Cui是個xml,Cuix是個壓縮包...知道好像沒什么好處,還是應該調用管理器操作而不是序列化操作.
關於宏的說明,看這個桌子的鏈接
特別的bug
public static class CuiStatic
{
#if NET35
//為了讓Acad2008配合高版本的API,所以寫了個拓展方法給它
public static bool RemovePartialMenu(this CustomizationSection cs, string fileName, string menuGroupName)
{
return cs.RemovePartialMenu(fileName);//卸載局部cui
}
#endif
}
然后 cs.RemovePartialMenu 這個萬惡之源,令我在測試Acad2021移除未融入的cuix的時候是失效的,為什么失效呢?
在我百思不得其解的時候,執行了一次"_.CuiUnLoad"自帶的卸載命令,它會將cuix某些錯誤修復了,
然后再執行 cs.RemovePartialMenu 是成功的了.
修改主要Cui和Cuix
using Autodesk.AutoCAD.Customization;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
//需要引用 AcCui.dll
namespace JoinBox.Menu
{
// 修改mainCui文件,可以考慮更換為修改局部cui替代
// 參考來自 https://adndevblog.typepad.com/autocad/2012/12/customizing-double-click-on-block-reference.html
public partial class Cmd
{
[CommandMethod("ChangeMacroCUI")]
public void ChangeMacroCUI()
{
var cs = Cui.CuiMain(out string expand);
//遍歷所有雙擊動作
DoubleClickAction ad = null;
foreach (DoubleClickAction dca in cs.MenuGroup.DoubleClickActions)
{
if (dca.Name == "塊" || dca.Name == "Block")
{
ad = dca;
var ma = ad.DoubleClickCmd.MenuMacroReference.macro;
#if true
ma.Command = "$M =$(if,$(and,$(>,$(getvar,blockeditlock),0)),^C^C_properties,^C^C_bedit)"; //原始的
#else
ma.Command = $"^C^C_{Cmd_MyBEdit}";//新創建的命令
#endif
}
if (dca.Name == "屬性塊")
{
ad = dca;
var ma = ad.DoubleClickCmd.MenuMacroReference.macro;
#if true
ma.Command = "^C^C_eattedit"; //原始的
#else
ma.Command = $"^C^C_{Cmd_MyBEdit}";//新創建的命令
#endif
}
}
if (cs.IsModified)
{
cs.SaveAs(cs.CUIFileName);//保存覆蓋掉主cui文件
CuiStatic.CuiAcedReloadMenus();
}
}
const string Cmd_MyBEdit = "MyBEdit";
[CommandMethod(Cmd_MyBEdit, CommandFlags.UsePickSet)]
public void MyBeditCommand()
{
var doc = Application.DocumentManager.MdiActiveDocument;
var ed = doc.Editor;
var db = doc.Database;
string regAppName = "MyApp";
ed.WriteMessage("\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
return;
var result = ed.GetSelection();
if (result.Status != PromptStatus.OK)
{
return;
}
var ss = result.Value;
foreach (SelectedObject so in ss)
{
bool isMyBlockRef = false;
db.Action(tr =>
{
var ent = so.ObjectId.ToEntity(tr);
// 讓我們檢查XDATA以識別我們是否需要顯示我們的對話框
if (ent.GetXDataForApplication(regAppName) != null)
{
isMyBlockRef = true;
}
});
if (isMyBlockRef)
{
Application.ShowAlertDialog("定制行動,如顯示我們的表格。");
}
else
{
// 讓Auto CAD進行塊編輯。
ObjectId[] ids = ss.GetObjectIds();
ed.SetImpliedSelection(ids);
doc.SendStringToExecute("_BEDIT ", false, false, false);
}
}
}
}
}
修改局部Cui和Cuix
大多數時候不需要修改主Cui而是用局部Cui,因為局部Cui會替代主Cui的動作.
using System.IO;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Customization;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
namespace JoinBox.Menu
{
// 修改局部cui文件
// 參考來自: https://through-the-interface.typepad.com/through_the_interface/2007/05/creating_a_part.html
public partial class Cmd
{
/// <summary>
/// 建立CUI菜單
/// </summary>
[CommandMethod("BuildMenuCUI")]
public void BuildMenuCUI()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
var pcfs = Cui.CuiMain(out string expand).PartialCuiFiles;//局部cui
string myCuiName = "Kean";
string myCuiFile = AutoGo.ConfigPath + myCuiName + expand;
string msg = $"\n自定義CUI文件: \"{myCuiFile}\" ";
if (pcfs.Contains(myCuiFile))
{
ed.WriteMessage(msg + "已經加載;");
ed.WriteMessage("\n執行卸載");
Cui.UnLoad(myCuiName);
ed.WriteMessage("\n卸載成功");
return;
}
if (File.Exists(myCuiFile))
{
ed.WriteMessage(msg + "存在,");
}
else
{
ed.WriteMessage(msg + "不存在建立它,");
var pcs = CuiCreate(myCuiName, "Attblockref");//屬性塊
pcs.SaveAs(myCuiFile); //保存文件可能因為c盤權限而失敗,更換到其他盤
}
ed.WriteMessage("執行加載\r");
Cui.Load(myCuiFile);
CuiStatic.CuiAcedReloadMenus();
}
/// <summary>
/// 創建CUI菜單雙擊
/// </summary>
/// <param name="cuiName">cui菜單的名稱</param>
/// <param name="dxfName">限定雙擊對象的類型,例如"Circle" "Attblockref"</param>
/// <returns></returns>
private static CustomizationSection CuiCreate(string cuiName, string dxfName)
{
// 為我們的部分菜單創建自定義部分
var pcs = new CustomizationSection
{
MenuGroupName = cuiName
};
string doubleClick = "DoubleClick";
//cui雙擊事件名稱...這里也是命令定義的名稱,需要用這個名稱去動態編譯一個命令出來...
string actionName = $"{doubleClick}ActionName_{cuiName}";
//創建雙擊動作...局部的定義會覆蓋掉主cui的定義,覆蓋順序未知
var dca = new DoubleClickAction(pcs.MenuGroup, actionName, -1)
{
Description = "雙擊自定義",
ElementID = "EID_" + actionName,
DxfName = dxfName
};
var macGroup = new MacroGroup($"MacroGroup_{cuiName}", pcs.MenuGroup);
//actionName是執行的命令,你把他當成命令的唯一身份證號碼
var createMenuMacro = macGroup.CreateMenuMacro(actionName, $"^C^C_{actionName}", $"ID_{dxfName}_{doubleClick}_{cuiName}");
var cmd = new DoubleClickCmd(dca)
{
MacroID = createMenuMacro.ElementID
};
dca.DoubleClickCmd = cmd;
return pcs;
}
//這里還沒有完成動態編譯.......就將就一下
const string Cmd_double = "DoubleClickActionName_Kean";//CuiCreate提供的...最好修改成動態編譯
[CommandMethod(Cmd_double)]
public void DoubleClickActionName_Kean()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
ed.WriteMessage("\n雙擊屬性塊執行了我耶");
}
}
}
Cui類
using System;
using System.Text;
using System.IO;
using Autodesk.AutoCAD.Customization;
using Autodesk.AutoCAD.ApplicationServices;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
namespace JoinBox.Menu
{
public class Cui
{
/// <summary>
/// 打開主CUI文件
/// </summary>
/// <param name="expand">拓展名</param>
/// <returns></returns>
public static CustomizationSection CuiMain(out string expand)
{
expand = "";
//打開主CUI文件,可以考慮局部cui文件
string old = (string)Acap.GetSystemVariable("MENUNAME");
var mainCuiFile = old;
if (mainCuiFile == null)
{
throw new ArgumentNullException(nameof(CuiMain));
}
if (File.Exists(mainCuiFile + ".cuix"))
{
mainCuiFile += ".cuix";//高版本
expand = ".cuix";
}
else if (File.Exists(mainCuiFile + ".cui"))
{
mainCuiFile += ".cui";//低版本08
expand = ".cui";
}
if (mainCuiFile == old)
{
throw new ArgumentNullException("沒有找到對應的cui文件:" + nameof(CuiMain));
}
//反序列化這個文件打開
return new CustomizationSection(mainCuiFile);
}
/// <summary>
/// 加載CUI文件
/// </summary>
/// <param name="cuiFile">cui文件路徑</param>
public static void Load(string cuiFile)
{
SendCmd("_.CuiLoad", new string[] { cuiFile });
//var cs = CuiMain(out string expand);
//cs.AddPartialMenu()//加載局部cui,加載類的
}
/// <summary>
/// 卸載CUI文件
/// </summary>
/// <param name="cuiMenuGroupName">組名</param>
public static void UnLoad(string cuiMenuGroupName)
{
// AccordingCadLispSendCmd("_.CuiUnLoad", new string[] { cuiMenuGroupName });
var cs = CuiMain(out string expand);
UnLoad(cs, cuiMenuGroupName);
}
/// <summary>
/// 卸載局部cui
/// </summary>
/// <param name="cs">MainCui</param>
/// <param name="removeCuiFile">指定移除的文件</param>
/// <param name="removeBadFile">移除壞掉的cui</param>
static void UnLoad(CustomizationSection cs, string removeCuiFile, bool removeBadFile = true)
{
var pcfs = cs.PartialCuiFiles;//局部cui
//移除壞掉的cui(刪除路徑的文件之后顯示未融入的)
if (removeBadFile)
{
for (int i = 0; i < pcfs.Count; i++)
{
var fileName = pcfs[i];
if (!File.Exists(fileName))
{
RemovePartialMenu(cs, pcfs, fileName);
i--;
}
}
}
//移除指定名稱的局部cui文件
for (int i = 0; i < pcfs.Count; i++)
{
var fileName = pcfs[i];
if (removeCuiFile.ToUpper() == GetFileName(fileName).ToUpper())
{
RemovePartialMenu(cs, pcfs, fileName);
i--;
}
}
if (cs.IsModified)
{
cs.Save();
}
}
/// <summary>
/// 移除局部cui
/// </summary>
/// <param name="cs"></param>
/// <param name="pcfs"></param>
/// <param name="fileName"></param>
static void RemovePartialMenu(CustomizationSection cs, PartialCuiFileCollection pcfs, string fileName)
{
//Acad2008執行RemovePartialMenu會移除了pcfs元素和MainCui的元素
//Acad2021執行RemovePartialMenu是true,但是沒有和Acad08一樣
var aa2 = cs.RemovePartialMenu(fileName, null);//移除局部cui
if (aa2 && pcfs.Contains(fileName))
{
pcfs.Remove(fileName);
// 如果Acad2021移除失敗了,證明了是第一次移除,可能存在cuix的錯誤導致.
// 利用lisp語句移除是正確的,此命令會導致成功修復了某些東西.
// 畢竟修復了一次之后執行上面就是正確的了,不知道怎么復現.
SendCmd("_.CuiUnLoad", new string[] { fileName });
// https://adndevblog.typepad.com/autocad/2012/07/unload-partial-cuix-when-autocad-quits.html
// 這句無法工作
// var aa3 = Acap.UnloadPartialMenu(fileName);//卸載局部cui
}
}
/// <summary>
/// 路徑或(文件名.后綴)獲取文件名,因為不存在cui路徑時候會未融入
/// </summary>
/// <param name="fileOrPath"></param>
/// <returns></returns>
static string GetFileName(string fileOrPath)
{
var a = fileOrPath.LastIndexOf('\\') + 1;
var b = fileOrPath.LastIndexOf('.');
var groupName = fileOrPath.Substring(a, b - a);
return groupName;
}
/// <summary>
/// 以lisp的方式發送命令
/// </summary>
/// <param name="cuiFile"></param>
static void SendCmd(string cmd, string[] args)
{
Document doc = Application.DocumentManager.MdiActiveDocument;
object oldCmdEcho = Application.GetSystemVariable("CMDECHO");
object oldFileDia = Application.GetSystemVariable("FILEDIA");
Application.SetSystemVariable("CMDECHO", 0);
Application.SetSystemVariable("FILEDIA", 0);
//界面還沒准備好導致這里會出錯
var arg = new StringBuilder();
foreach (var item in args)
{
arg.Append(item);
}
doc.SendStringToExecute(cmd + " " + arg + " ", false, false, false);
doc.SendStringToExecute("(setvar \"FILEDIA\" " + oldFileDia.ToString() + ")(princ) ", false, false, false);
doc.SendStringToExecute("(setvar \"CMDECHO\" " + oldCmdEcho.ToString() + ")(princ) ", false, false, false);
}
}
}
CuiStatic類
using System.Runtime.InteropServices;
using Autodesk.AutoCAD.Customization;
namespace JoinBox.Menu
{
public static class CuiStatic
{
#if NET35
//為了讓Acad2008配合高版本的API,所以寫了個拓展方法給它
public static bool RemovePartialMenu(this CustomizationSection cs, string fileName, string menuGroupName)
{
return cs.RemovePartialMenu(fileName);//卸載局部cui
}
#endif
#if NET35
/// <summary>
/// 重新載入CUI文件,刷新當前工作區2008
/// </summary>
/// <returns></returns>
[DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedReloadMenus")]
public static extern int CuiAcedReloadMenus();
#else
/* 這里未能詳盡所有的版本 .*/
/// <summary>
/// 重新載入CUI文件,刷新當前工作區2021
/// </summary>
/// <returns></returns>
[DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedReloadMenus@@YAX_N@Z")]
public static extern int CuiAcedReloadMenus();
#endif
}
}
20210528更新,詳盡所有的版本
上面的 /* 這里未能詳盡所有的版本 .*/ 標記的入口點不一樣,但是有規律,所以可以參考此處代碼:
JoinBoxCurrency.PeInfo 參考 測試篇 c#讀PE32PE32+
namespace JoinBox.Menu
{
public static class CuiStatic
{
/// <summary>
/// 重新載入CUI文件,刷新當前工作區
/// </summary>
/// <returns></returns>
public static int CuiAcedReloadMenus()
{
string funcName = "acedReloadMenus";
// 路徑通過程序集就能拿到
var acadexeName = Process.GetCurrentProcess().MainModule.FileName;
bool getYes = false;
//輸出所有的函數名
var pe = new JoinBoxCurrency.PeInfo(acadexeName);
var sb = new StringBuilder();
foreach (var name in pe.ExportDirectory.NameList)
{
sb.Append(Environment.NewLine);
var str = Encoding.Default.GetString(name as byte[]);
if (str.Contains(funcName))
{
funcName = str;
getYes = true;
break;
}
sb.Append(str);
}
// Debug.WriteLine(sb.ToString());
if (!getYes)
{
throw new Exception("沒有找到對應的函數");
}
IntPtr hModule = Win32API.WinApi.GetModuleHandle(acadexeName); // 執行前必須加載了先...
if (hModule == IntPtr.Zero)
throw new Exception("找不到模塊:" + acadexeName + "當前程序沒有加載這個東西?");
//函數指針
IntPtr funcAdress = Win32API.WinApi.GetProcAddress(hModule, funcName);
if (funcAdress == IntPtr.Zero)
throw new Exception("找不到函數入口點:" + funcAdress);
//利用委托調用函數指針,從而實現對方法的調用.
var deFunc = Marshal.GetDelegateForFunctionPointer(funcAdress, typeof(DelegateAcedReloadMenus)) as DelegateAcedReloadMenus;
return deFunc.Invoke();//調用方法(刷新cad的cui)
}
//委托,調用函數指針
delegate int DelegateAcedReloadMenus();
}}
ToolBar工具條
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.Customization;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
namespace JoinBox.Menu
{
public class ToolBar
{
[CommandMethod("TestToolBarClose")]
public void TestToolBarClose()
{
var cuiMain = Cui.CuiMain(out string expand);
ToolBarGetExistsPrint(cuiMain);
//關閉工具條
ToolBarClose(cuiMain, "YQARCH");
//ToolBarClose(cuiMain, "MINI");
//ToolBarClose(cuiMain, "ACAD");
}
/// <summary>
/// 打印當前工作空間顯示的工具條id
/// </summary>
public static void ToolBarGetExistsPrint(CustomizationSection cs)
{
List<string> lst = new();
//當前使用的工作空間
string wscurrent = (string)Application.GetSystemVariable("wscurrent");
foreach (Workspace pace in cs.Workspaces)//工作空間 "二維草圖與注釋"
{
var cs2 = pace.CustomizationSection;//空間下的cui
if (wscurrent != pace.Name)//當前空間
{
continue;
}
Debug.WriteLine("**當前使用的空間名字:" + pace.Name);
Debug.WriteLine("**此空間的Cui文件:" + cs2.CUIFileName);
foreach (Toolbar tb in cs2.MenuGroup.Toolbars)
{
if (tb.ToolbarVisible == ToolbarVisible.hide)
{
lst.Add(tb.ElementID);
}
}
break;
//foreach (WorkspaceToolbar tb in pace.WorkspaceToolbars)//工具條
//{
// if (tb.Display == 0)//會遍歷到其他空間可展示的東西
// {
// lst.Add(tb.ElementID);
// }
//}
}
//按照字母排序,打印目前已經存在的工具條
lst = lst.OrderBy(elementID => elementID).ToList();
foreach (var item in lst)
{
Debug.WriteLine(item);
}
}
/// <summary>
/// 關閉工具條
/// </summary>
public static void ToolBarClose(CustomizationSection cs, string cuiMenuGroupName)
{
//當前使用的工作空間
string wscurrent = (string)Application.GetSystemVariable("wscurrent");
foreach (Workspace pace in cs.Workspaces)//工作空間 "二維草圖與注釋"
{
if (wscurrent != pace.Name)//當前空間
{
continue;
}
foreach (WorkspaceToolbar tb in pace.WorkspaceToolbars)//工具條
{
if (tb.MenuGroup == cuiMenuGroupName)
{
pace.WorkspaceToolbars.Remove(tb);//卸載工具條
}
}
break;
}
}
}
}
相關閱讀
[尼克勞斯] https://www.cnblogs.com/bomb12138/p/3607990.html
(完)
