AutoCAD.NET 不使用P/Invoke方式調用acad.exe或accore.dll中的接口(如acedCommand、acedPostCommand等)


使用C#進行AutoCAD二次開發,有時候由於C#接口不夠完善,或者低版本AutoCAD中的接口缺少,有些工作不能直接通過C#接口來實現,所以需要通過P/Invoke的方式調用AutoCAD的其他DLL中的接口來實現。

最常見的是向AutoCAD發送同步命令,在高版本的AutoCAD.NET接口中好像是可以發送同步命令了(據說是從2014或2015開始是可以了,不過我沒有進行驗證),但在低版本AutoCAD.NET中是沒有發送同步命令接口的,SendStringToExecute和COM接口中的SendCommand都是異步操作,只有通過acedCmd、acedCommand、acedPostCommand才可能發送同步命令,這三個接口在AutoCAD2013之前是在acad.exe中的,而從AutoCAD2013開始放到了accore.dll中,並且acedPostCommand這個接口是官方沒有公布但實際是可以使用的。

正常通過P/Invoke方式調用需要進行以下聲明:

AutoCAD2013以前的版本

[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acad.exe", EntryPoint = "acedCmd", CallingConvention = CallingConvention.Cdecl)]
private static extern int acedCmd(IntPtr vlist);
[DllImport(
"acad.exe", EntryPoint = "acedCommand")] private static extern int acedCommand(IntPtr vlist);
//CAD2008和2008以前版本中的EntryPoint名稱和2009以及2009之后的版本不一樣 [DllImport("acad.exe", CharSet = CharSet.Auto, EntryPoint = "?acedPostCommand@@YAHPB_W@Z", CallingConvention = CallingConvention.Cdecl)] private static extern int acedPostCommand(string strExpr);
//CAD2008之后的版本中 [DllImport(
"acad.exe", CharSet = CharSet.Auto, EntryPoint = "?acedPostCommand@@YAHPEB_W@Z", CallingConvention = CallingConvention.Cdecl)] private static extern int acedPostCommand(string strExpr);

AutoCAD2013及更新版本

[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("accore.dll", EntryPoint = "acedCmd", CallingConvention = CallingConvention.Cdecl)]
private static extern int acedCmd(IntPtr vlist);

[DllImport("accore.dll", EntryPoint = "acedCommand")]
private static extern int acedCommand(IntPtr vlist);

//CAD2008和2008以前版本中的EntryPoint名稱和2009以及2009之后的版本不一樣
[DllImport("accore.dll", CharSet = CharSet.Auto, EntryPoint = "?acedPostCommand@@YAHPB_W@Z", CallingConvention = CallingConvention.Cdecl)]
private static extern int acedPostCommand(string strExpr);
//CAD2008之后的版本中
[DllImport("accore.dll", CharSet = CharSet.Auto, EntryPoint = "?acedPostCommand@@YAHPEB_W@Z", CallingConvention = CallingConvention.Cdecl)]
private static extern int acedPostCommand(string strExpr);

使用時直接進行調用即可:

ResultBuffer rb = new ResultBuffer();
rb.Add(new TypedValue((int)LispDataType.Text, "_.mycmd"));
int i = acedCmd(rb.UnmanagedObject);

要注意的是這些接口不能使用Loadlibrary的方式進行調用,因為Loadlibrary會把指定的DLL或EXE進行加載,加載后應該和當前程序所寄存的AutoCAD主進程不是一回事了,調用會有問題或調用結果達不到預期效果。

 

今天要介紹的是另外一種不使用P/Invoke方式就可以在自己的插件中調用acad.exe或accore.dll中接口的方法,主要思路如下:

1、獲取當前進程Process,因為插件是被加載到AutoCAD中運行的,所以獲取到的是AutoCAD進程。

2、獲取當前進程加載的所有模塊ProcessModuleCollection。

3、遍歷ProcessModuleCollection找到自己要調用的接口所在的模塊ProcessModule。

4、聲明一個和要調用的接口格式相同的委托。

5、獲取ProcessModule的EntryPointAddress,然后使用Marshal.GetDelegateForFunctionPointer方法根據函數句柄得到一個委托實例。

6、調用委托實例進行執行即可。

以acedPostCommand為例完整代碼如下:

//此處聲明的委托和acedPostCommand(string strExpr)不相同是因為經過測試,如果參數聲明為string類型,調用的時候傳送到CAD的命令是亂碼
public delegate int DelegateAcedPostCommand(byte[] cmd);
static DelegateAcedPostCommand pc;

public void ShowMsg() 
{
    //不同版本接口所在的程序模塊不同
    string mdName = Application.Version.Major >= 19 ? "accore.dll" : "acad.exe";
    //CAD2007和2008中接口入口點名稱不一樣,2007以前的沒有看,想看的可以用depends工具查看
    string apiName = (Application.Version.Major >= 17 && Application.Version.Minor <= 1) ? "?acedPostCommand@@YAHPB_W@Z" : "?acedPostCommand@@YAHPEB_W@Z";
    //獲取當前CAD進程
    Process pro = Process.GetCurrentProcess();
    //獲取CAD加載的所有程序模塊(引用了什么DLL、EXE)
    ProcessModuleCollection pmc = pro.Modules;
    IntPtr iptr = IntPtr.Zero;
    for (int i = 0; i < pmc.Count; i++)
    {
        ProcessModule pm = pmc[i];
        if (pm.ModuleName == mdName)
        {
             iptr = pm.EntryPointAddress;
             break;
         }
    }
    if (iptr != IntPtr.Zero)
    {
        pc = (DelegateAcedPostCommand)Marshal.GetDelegateForFunctionPointer(iptr, typeof(DelegateAcedPostCommand));
        if (pc != null)
        {
             string str = "_.remmenu\n";
//此處要把字符串使用Unicode編碼轉換為byte[],然后再傳入委托接口,不然到CAD之后會亂碼
byte[] bytes = Encoding.Unicode.GetBytes(str); int i = pc.Invoke(bytes); } } }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM