用编程方式调用AutoCAD命令的技术

 

It's quite common to want to call commands from one or other of AutoCAD's programming environments. While it's cleanest (from a purist's perspective) to use an API to perform the task you want, the quickest way - and the one which will appeal to the pragmatists (and the lazy) among us is often to call a sequence of commands. And there are, of course, a few places in AutoCAD where APIs have not been exposed, so scripting commands is the only way to achieve what you want.
想在AutoCAD的各种编程环境中调用命令是非常平常的事,但是使用API来完成你想要做的事是非常简洁的(纯论者的观点),最快捷的方式——对于像我们这样的实用主义者(和懒惰的人)来说是经常调用一系列命令。并且,当然,在AutoCAD中有一些地方的API还没有开放,所以达到目的的唯一途径就是使用脚本命令了。

Let's take the simple example of adding a line. Here's what you'd do from the different environments from a low-level API perspective:
让我们举一个简单的添加直线的例子,下面是在不同的环境中,按照低层API的观点来说你应该做的:

  • LISP - create an association list representing the entity and then (entmake) it
  • LISP——创建一个关联列表来表示实体然后执行它
  • VBA (or COM) - get the ModelSpace object from the active drawing and call AddLine (the COM API is probably the simplest in that respect)
  • VBA(or COM)——从当前激活的图形中获得ModelSpace对象,然后执行AddLine(COM API大概都是这么简单)
  • .NET and ObjectARX - open the block table and then the model-space block table record, create a new line object and append it to the model-space (and to the transaction, if you're using them), closing the various objects along the way
  • .NET and ObjectARX ——打开块表(block table)然后再打开模型空间(model-space)块表(block table)记录,创建一个新的直线对象并且将这个对象添加到模型空间里(model-space)(如果你用事务的话就通过事务来完成),关闭这一过程中一系列的对象

Having first started coding for AutoCAD with LISP (for R10), I know that the simplest way to do what you want from that environment is to call the LINE command, passing in the start and end points:

(command "_LINE" "0,0,0" "100,100,0" "")

LISP is great in that respect: as you're not able to define native commands (only LISP) functions, it's perfectly acceptable to use it to script commands to do what you want, rather than rely on low-level APIs.

ObjectARX in particular has potential issues with respect to defining native commands calling commands, as AutoCAD is only "re-entrant" up to 4 levels. Without going into specifics, it's basically best to avoid calling commands using acedCommand() from ObjectARX, unless the command is registered as a LISP function using acedDefun().

While you do have to be careful when calling commands from VBA or ObjectARX, there are a few options available to you.

ObjectARX

  • ads_queueexpr()
    • This old favourite is intended to be used from acrxEntryPoint() to execute a sequence of commands after (s::startup) has been called from LISP (as you are not able to use acedCommand() from this context)
    • You need to declare it yourself (extern "C" void ads_queueexpr( ACHAR *);) before use
    • It has been unsupported as long as I can remember, but is widely-used and mentioned in the Tips & Techniques section of the ObjectARX Developer's Guide
  • AcApDocManager::sendStringToExecute()
    • This function has the advantage of a few more options being available as arguments, mainly around where to execute the string (which document, and whether it be activated), and whether to echo the string to the command-line
  • ::SendMessage()
    • This is a standard Win32 platform API and so can, in effect, be used to drive AutoCAD from an external client. It uses a structure to pass the string that is often a bit tricky to set up (it became a migration issue when we switched to Unicode, for example)
  • IAcadDocument::SendCommand()
    • This COM method is the only way (other than acedCommand() or acedCmd()) to execute a command synchronously from AutoCAD (and even then it may not be completely synchronous if requiring user input)
  • acedCommand()
    • This is the ObjectARX equivalent to (command), and is genuinely synchronous. Unfortunately (as mentioned earlier) there are issues with using it directly from a natively-registered command, so I'd recommend only using it from acedDefun()-registered commands (see the ObjectARX documentation and the below sample for more details)

VBA (some of which also applies to VB)

  • ThisDrawing.SendCommand
    • This is the same as IAcadDocument::SendCommand() from C++
  • SendKeys
    • This is just a simple technique to send key-strokes to the command-line
  • SendMessage
    • This is just the Win32 API mentioned above, but declared and called from VB(A)

So, now for some sample code...


ObjectARX sample code

The first can be dropped into an ObjectARX Wizard-defined project (I used Visual Studio 2005 and ObjectARX 2007). You'll need to make sure "COM-client" is selected and you name your project "SendingCommands" (or you search and replace to change the name in the below code to something you prefer).

The code creates points along a line (from 0,0 to 5,5), using different techniques to send the command to AutoCAD. I would, of course, use the proper ObjectARX APIs to do this (creating an AcDbPoint etc.) - I just used this as an example of a command that could be sent.

It creates the first point on load, using ads_queueexpr(), and defines commands (TEST1, TEST2, TEST3 and TEST4) for the subsequent tests (the last being an acedDefun()-registered command).


#include "StdAfx.h"

#include "resource.h"

 

#define szRDS _RXST("Adsk")

 

extern "C" void ads_queueexpr( ACHAR *);

 

//----- ObjectARX EntryPoint

class CSendingCommandsApp : public AcRxArxApp {

 

public:

  CSendingCommandsApp () : AcRxArxApp () {}

 

  virtual AcRx::AppRetCode On_kInitAppMsg (void *pkt) {

    // You *must* call On_kInitAppMsg here

    AcRx::AppRetCode retCode =AcRxArxApp::On_kInitAppMsg (pkt) ;

    return (retCode) ;

  }

 

  virtual AcRx::AppRetCode On_kUnloadAppMsg (void *pkt) {

    // You *must* call On_kUnloadAppMsg here

    AcRx::AppRetCode retCode =AcRxArxApp::On_kUnloadAppMsg (pkt) ;

    return (retCode) ;

  }

 

  virtual AcRx::AppRetCode On_kLoadDwgMsg(void * pkt) {

    AcRx::AppRetCode retCode =AcRxArxApp::On_kLoadDwgMsg (pkt) ;

    ads_queueexpr( _T("(command/"_POINT/" /"1,1,0/")") );

    return (retCode) ;

  }

 

  virtual void RegisterServerComponents () {

  }

 

public:

 

  // - AdskSendingCommands._SendStringToExecTest command (do not rename)

  static void AdskSendingCommands_SendStringToExecTest(void)

  {

    acDocManager->sendStringToExecute(curDoc(), _T("_POINT 2,2,0 "));   

  }

 

  static void SendCmdToAcad(ACHAR *cmd)

  {

    COPYDATASTRUCT cmdMsg;

    cmdMsg.dwData = (DWORD)1;

    cmdMsg.cbData = (DWORD)(_tcslen(cmd) + 1) * sizeof(ACHAR);

    cmdMsg.lpData = cmd;

    SendMessage(adsw_acadMainWnd(), WM_COPYDATA, NULL, (LPARAM)&cmdMsg);

  }

 

  // - AdskSendingCommands._SendMessageTest command (do not rename)

  static void AdskSendingCommands_SendMessageTest(void)

  {

    SendCmdToAcad(_T("_POINT 3,3,0 "));

  }

 

  // - AdskSendingCommands._SendCommandTest command (do not rename)

  static void AdskSendingCommands_SendCommandTest(void)

  {

    try {

      IAcadApplicationPtr pApp = acedGetIDispatch(TRUE);

      IAcadDocumentPtr pDoc;

      pApp->get_ActiveDocument(&pDoc);

      pDoc->SendCommand( _T("_POINT 4,4,0 ") );

    }

    catch(_com_error& e) {

      acutPrintf(_T("/nCOM error: %s"), (ACHAR*)e.Description());

    }

  }

 

  // ----- ads_test4 symbol (do not rename)

  static int ads_test4(void)

  {

    acedCommand(RTSTR, _T("_POINT"), RTSTR,_T("5,5,0"), RTNONE);

    acedRetVoid();

    return (RSRSLT);

  }

} ;

 

// ----------------------------------------------------------

 

 

ACED_ARXCOMMAND_ENTRY_AUTO(CSendingCommandsApp, AdskSendingCommands, _SendStringToExecTest, TEST1, ACRX_CMD_TRANSPARENT, NULL)

ACED_ARXCOMMAND_ENTRY_AUTO(CSendingCommandsApp, AdskSendingCommands, _SendMessageTest, TEST2, ACRX_CMD_TRANSPARENT, NULL)

ACED_ARXCOMMAND_ENTRY_AUTO(CSendingCommandsApp, AdskSendingCommands, _SendCommandTest, TEST3, ACRX_CMD_TRANSPARENT, NULL)

ACED_ADSCOMMAND_ENTRY_AUTO(CSendingCommandsApp, test4, true)

// ----------------------------------------------------------

IMPLEMENT_ARX_ENTRYPOINT(CSendingCommandsApp)


VBA sample code

This sample defines VBA macros that create points from 6,6 to 8,8, using techniques that mirror the ones shown in the ObjectARX sample.


Option Explicit On

 

 

Private Const WM_COPYDATA = &H4A

 

 

Private Type COPYDATASTRUCT

    dwData As Long

    cbData As Long

    lpData As String

End Type

 

 

Private Declare Function SendMessage Lib "user32" Alias _

  "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal _

  wParam As Long, ByVal lParam As Any) As Long

 

 

Public Sub SendMessageToAutoCAD(ByVal message As String)

    Dim data As COPYDATASTRUCT

    Dim str As String

    str = StrConv(message, vbUnicode) 'converts to Unicode

 

    data.dwData = 1

    data.lpData = str

    data.cbData = (Len(str) + 2)

 

    SendMessage(ThisDrawing.Application.hwnd, WM_COPYDATA, 0, data)

End Sub

 

 

Public Sub Test4()

    ThisDrawing.SendCommand("_point 6,6,0 ")

End Sub

 

 

Public Sub Test5()

    SendKeys("_point 7,7,0 ")

End Sub

 

 

Public Sub Test6()

    SendMessageToAutoCAD("_point 8,8,0 ")

End Sub

 

 
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值