Some developers defined the ARX command (by AddCommand )which invokes acedCommand and calls the ARX command by LISP (way1). In the previous release such as AutoCAD 2007, this caused the failure after running the LISP command several times, the solution is to define ARX command with acedDefun (way2)instead of AddCommand. This solution still works well in the latest releases.
This is a sample project: Download LispArx_VS2008. It also includes way1. Although way1 seems to work as well in the latest releases, it is still not a recommended way.
To test:
1. Build the attached ARX application and load it into AutoCAD.
2. Load test.lsp.
3. Run test1 or test2 command some times
NOTE: You may want to inspect the CMDNAMES variable after each successful execution and note when AutoCAD does not properly exit the ARX command.
Some key codes:
// lisp2cmd.cpp : Initialization functions
#include "StdAfx.h"
#include "StdArx.h"
#include "resource.h"
HINSTANCE _hdllInstance =NULL ;
// This command registers an ARX command.
void AddCommand(const TCHAR* cmdGroup,
const TCHAR* cmdInt,
const TCHAR* cmdLoc,
const int cmdFlags,
const AcRxFunctionPtr cmdProc,
const int idLocal = -1);
// NOTE: DO NOT edit the following lines.
//{{AFX_ADS_FUNC_TABLE
typedef struct {
TCHAR *name;
int (*fptr)();
BOOL regFunc;
BOOL renderCmd;
} ftblent;
#define ELEMENTS(array) (sizeof(array)/sizeof((array)[0]))
//the functions for acedDeFun
ftblent exfun[] = {
{_T(""), NULL, FALSE, FALSE },
{_T("c:testcmd"), testcmd, FALSE, FALSE },
}; //}}AFX_ADS_FUNC_TABLE
//{{AFX_ARX_MSG
void InitApplication();
void UnloadApplication();
//}}AFX_ARX_MSG
// NOTE: DO NOT edit the following lines.
//{{AFX_ARX_ADDIN_FUNCS
//}}AFX_ARX_ADDIN_FUNCS
//////////////////////////////////////////////////////////
// DLL Entry Point
extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
_hdllInstance = hInstance;
} else if (dwReason == DLL_PROCESS_DETACH) {
}
return TRUE; // ok
}
///////////////////////////////////////////////// ObjectARX EntryPoint
extern "C" AcRx::AppRetCode
acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt)
{
switch (msg) {
case AcRx::kInvkSubrMsg:
dofun();
break;
case AcRx::kUnloadDwgMsg:
funcunload();
break;
case AcRx::kLoadDwgMsg:
funcload();
break;
case AcRx::kInitAppMsg:
// Comment out the following line if your
// application should be locked into memory
acrxDynamicLinker->unlockApplication(pkt);
acrxDynamicLinker->registerAppMDIAware(pkt);
InitApplication();
break;
case AcRx::kUnloadAppMsg:
UnloadApplication();
break;
}
return AcRx::kRetOK;
}
// Init this application. Register your
// commands, reactors...
void InitApplication()
{
// NOTE: DO NOT edit the following lines.
//{{AFX_ARX_INIT
AddCommand(_T("ASDKCMDS"), _T("TESTCMD"), _T("TESTCMD"), ACRX_CMD_TRANSPARENT, AsdkCMDStestcmd);
//}}AFX_ARX_INIT
// TODO: add your initialization functions
}
// Unload this application. Unregister all objects
// registered in InitApplication.
void UnloadApplication()
{
// NOTE: DO NOT edit the following lines.
//{{AFX_ARX_EXIT
acedRegCmds->removeGroup(_T("ASDKCMDS"));
//}}AFX_ARX_EXIT
// TODO: clean up your application
}
// This functions registers an ARX command.
// It can be used to read the localized command name
// from a string table stored in the resources.
void AddCommand(const TCHAR* cmdGroup,
const TCHAR* cmdInt,
const TCHAR* cmdLoc,
const int cmdFlags,
const AcRxFunctionPtr cmdProc,
const int idLocal)
{
TCHAR cmdLocRes[65];
// If idLocal is not -1, it's treated as an ID for
// a string stored in the resources.
if (idLocal != -1) {
// Load strings from the string table and register the command.
::LoadString(_hdllInstance,
idLocal,
cmdLocRes, 64);
acedRegCmds->addCommand(cmdGroup,
cmdInt,
cmdLocRes,
cmdFlags,
cmdProc);
} else
// idLocal is -1, so the 'hard coded'
// localized function name is used.
acedRegCmds->addCommand(cmdGroup,
cmdInt,
cmdLoc, cmdFlags, cmdProc);
}
/////////////////////////////////////////////////////////////////////
// funcload(internal)
// This function is called to define all function names in the ADS
// function table. Each named function will be callable from lisp or
// invokable from another ADS application.
/////////////////////////////////////////////////////////////////////
static int funcload(void)
{
int i;
for (i = 1; i < ELEMENTS(exfun); i++) {
if (!acedDefun(exfun[i].name, i))
return RTERROR;
if (exfun[i].regFunc)
acedRegFunc(exfun[i].fptr, i);
}
return RTNORM;
}
/////////////////////////////////////////////////////////////////////
// funcunload(internal)
// This function is called to undefine all function names in the ADS
// function table. Each named function will be removed from the
// AutoLISP hash table.
/////////////////////////////////////////////////////////////////////
static int funcunload(void)
{
int i;
// Undefine each function we defined
for (i = 1; i < ELEMENTS(exfun); i++) {
acedUndef(exfun[i].name,i);
}
return RTNORM;
}
/////////////////////////////////////////////////////////////////////
// dofun(internal)
// This function is called to invoke the function which has the
// registerd function code that is obtained from acedGetFunCode.
/////////////////////////////////////////////////////////////////////
static int dofun(void)
{
int val,
rc;
acedRetVoid();
if ((val = acedGetFunCode()) < 1 || val > ELEMENTS(exfun))
return RTERROR;
#ifdef RENDER
if (exfun[val].renderCmd)
if (!InitRender(false))
return RTERROR;
#endif
rc = (*exfun[val].fptr)();
return ((rc == RTNORM) ? RSRSLT:RSERR);
}
// ObjectARX defined commands
#include "StdAfx.h"
#include "StdArx.h"
// This is command 'TESTCMD'
void AsdkCMDStestcmd()
{
ads_command(RTSTR, _T("_.LINE"), RTSTR, _T("0,0"), RTSTR, _T("100,100"), RTSTR, _T(""), RTNONE);
}
// This is command 'C:TESTCMD'
int testcmd()
{
ads_command(RTSTR, _T("_.LINE"), RTSTR, _T("0,0"), RTSTR, _T("100,100"), RTSTR, _T(""), RTNONE);
return RTNORM;
}