Class 9 - Interacting with AutoCAD
On the last class I have presented how to perform selection sets. This class I will show how can you interact with AutoCAD using global functions and acquiring information such as numbers, coordinates, system variables and much more.
Invoking Commands
ObjectARX provide us two global functions that allows us to invoke registered commands. This functionality is very handy and will help users to perform quick operations that don't require complex procedures. Even this method is quite simple you should avoid using it in complex and huge operations. This method may also create problems when dealing with events handling.
The two provided functions are acedCmd() and acedCommand(). The first one invokes the command through a passed in resbuf list which will inform all command parameters. The second function will receive a variable number of parameters which will reproduce the way you fire the command from the prompt interface. Below are these functions signature:
int acedCmd(const struct resbuf * rbp);
int acedCommand(int rtype, ... unnamed);
To build the resbuf list when using acedCmd() there is a utility function called acutBuildList() which constructs this linked list easily. You just need to pass paired values with codes that describe the types and end the list with a 0 or RTNONE value. Another good practice is to clear the command prompt, calling acedCommand(RTNONE) , after issued the command. Don't forget to free memory used, when using resbuf pointers, through the acutRelRb() utility function to avoid memory leaks. There are several ways to use theses functions and I will show some of them below:
acedCmd():
a) Moving the last created entity based on (0,0,0):
ads_point pt;
pt[0] = pt[1] = pt[2] = 0.0;
struct resbuf *Mv;
Mv = acutBuildList(RTSTR,_T("_MOVE"),RTSTR,_T("_LAST"),RTSTR,_T(""),
RTPOINT,pt,RTSTR,PAUSE,0);
acedCmd(Mv);
acedCommand(RTNONE);
acutRelRb(Mv);
b) Calling a "redraw" native command:
acedCommand():struct resbuf *cmdlist;
cmdlist = acutBuildList(RTSTR, _T("_REDRAW"), 0);
acedCmd(cmdlist);
acedCommand(RTNONE);
acutRelRb(cmdlist);
a) Calling a ZOOM command and pausing for user input:
acedCommand(RTSTR, _T("Zoom"), RTSTR, PAUSE, RTNONE);
b) Creating both a Circle and a Line entities:
System VariablesacedCommand(RTSTR, _T("circle"), RTSTR, _T("10,10"),RTSTR, PAUSE,
RTSTR, _T("line"), RTSTR, _T("10,10"), RTSTR, _T("20,20"),
RTSTR, _T(""), 0);
Your application will probably need to access AutoCAD system variables that can be read or write. ObjectARX provide two functions to deal with these variables using the resbuf structure to access and/or modify values. The function are called acedGetVar() and acedSetVar() and below are their signatures:
int acedGetVar(const ACHAR * sym,struct resbuf * result);
int acedSetVar(const ACHAR * sym,const struct resbuf * val);
The first parameter is the variable name the second the resbuf pointer to set / get information. The following example show how to change the FILLET radius which is stored through a system variable:
It is very important that you specify the correct type of resbuf item acquired.struct resbuf rb, rb1;
acedGetVar(_T("FILLETRAD"), &rb);
rb1.restype = RTREAL;
rb1.resval.rreal = 1.0;
acedSetVar(_T("FILLETRAD"), &rb1);
In this case, the FILLET radius is a real number which is RTREAL type.
User Input Functions
There are additional global functions to allow interaction with users via command prompt interface. Each of these functions could be used alone or with other ones. The following table shows what each function does:
acedGetInt | Gets an integer value |
acedGetReal | Gets a real value |
acedGetDist | Gets a distance |
acedGetAngle | Gets an angle (oriented to 0 degrees specified by the ANGBASE) |
acedGetOrient | Gets an angle (oriented to 0 degrees at the right) |
acedGetPoint | Gets a point |
acedGetCorner | Gets the corner of a rectangle |
acedGetKword | Gets a keyword |
acedGetString | Gets a string |
Each of these functions returns a int number as a result code that could be one of the following:
RTNORM | User entered a valid value |
RTERROR | The function call failed |
RTCAN | User entered ESC |
RTNONE | User entered only ENTER |
RTREJ | AutoCAD rejected the request as invalid |
RTKWORD | User entered a keyword or arbitrary text |
AutoCAD allows you to prevent invalid values when user respond to your input functions. This feature can be made through the acedInitGet() function which can receive one or a combination of the following values:
RSG_NONULL | Disallow null input |
RSG_NOZERO | Disallow zero values |
RSG_NONEG | Disallow negative values |
RSG_NOLIM | Do not check drawing limits, even if LIMCHECK is on |
RSG_DASH | Use dashed lines when drawing rubber-band line or box |
RSG_2D | Ignore Z coordinate of 3D points (acedGetDist() only) |
RSG_OTHER | Allow arbitrary input—whatever the user enters |
The following example shows how to acquire a value greater than zero:
int age = -1;
acedInitGet(RSG_NONULL RSG_NOZERO RSG_NONEG, NULL);
acedGetInt(_T("How old are you? "), &age);




41 Comments:
Hi,
Saying about interacting with AutoCAD I would indicate the Jig class. Sometimes it’s pretty handy.
By
Anonymous, at 2:17 PM
I would like to set all the system variables in AutoCAD using arx, so that all users in the network of our company use the same settings. Can you please help me on that.
Thanks
By
Anonymous, at 11:10 AM
Hello,
There are two utility functions to handle this: acedSetVar() and acedGetVar().
A simple example would be disable OSNAP during some operation and enable it back with old setup:
struct resbuf OldOsnap, NewOsnap;
// Get current OSNAP
acedGetVar("OSMODE", &OldOsnap);
// Chage OSNAP settings
NewOsnap.restype = RTSHORT;
NewOsnap.resval.rint = 0;
acedSetVar("OSMODE", &NewOsnap);
// Do something...
// Get back the old OSNAP
acedSetVar("OSMODE", &OldOsnap);
Regards,
Fernando.
By
Fernando Malard, at 2:11 PM
Hi,
My application is accepting list of files from user. and according to
need it opens the files automatically to accept user input by selecting
entites.
it have a function
AcApDocument * asOpenFile(CString FileToOpen)
which checkes wheather the file is already open. if not then, the files
is opened.
it works well but after switching 3-4 drawings while executing
executeInApplicationContext()
normally execution control comes out..
but 3-4 times execution control stuck their, untill, user manually
switch to previous drawing.
when I used
acDocManager->setCurDocument()
it gives fatal error "Command can not be nested more than 4 times" and
crashes
this are the procedures
void asOpenSelFile(void* pData)
{
Acad::ErrorStatus es = Acad::eOk;
if(acDocManager->isApplicationContext())
es = acDocManager->appContextOpenDocument((const char *)pData);
}
AcApDocument * asOpenFile(CString FileToOpen)
{
char* pStr = NULL;
AcApDocument *pDoc = NULL;
if(!asGetDocFromFilename(FileToOpen, pDoc))
{
pStr = FileToOpen.GetBuffer(FileToOpen.GetLength());
//Problem sentence
acDocManager->executeInApplicationContext(asOpenSelFile, (void *)pStr);
//--------------------------------------------------------------------------
ShowMessage(gpAsdkAppDocGlobals->m_sDocIniCommand);
FileToOpen.ReleaseBuffer();
asGetDocFromFilename(FileToOpen, pDoc);
}
else
{
acDocManager->sendStringToExecute(acDocManager->mdiActiveDocument(),
"\003\003\003", false, true);
acDocManager->activateDocument(pDoc);
}
return pDoc;
}
Thanks in advance
Abhay
By
Abhay Joshi, at 9:41 AM
Hello Abhay,
Could you build a sample with this code, reproducing the problem, and send me at fpmalard@yahoo.com.br ?
This way I can help you faster.
Regards,
Fernando.
By
Fernando Malard, at 10:24 AM
Hi,
When I use the acedGetPoint() function the AutoCAD hangs. This is the exact line.
acedGetPoint(NULL, "Enter Start Point", startPt);
acedGetPoint(startPt, "Enter Start Point", endPt);
I am using AutoCAD 2000 and Visual Studio 6. Why does the AutoCAD hang ?
Jatin.
By
Anonymous, at 4:53 PM
Hi Jatin,
I don't think this is the culprit.But acedGetPoint() should be something like:
ads_point pt1;
int res = acedGetPoint(NULL,"Message",pt1);
if (res == RTNORM)
{
...
}
Maybe you need to check what is happening before your code reach this point.
Regards,
Fernando.
By
Fernando Malard, at 5:13 PM
Hi,
Here is the code.
AcRx::AppRetCode
acrxEntryPoint(AcRx::AppMsgCode msg, void* appId)
{
AcDbObjectId lineId;
ads_point startPt, endPt;
AcGePoint3d geStartPt, geEndPt;
switch(msg) {
case AcRx::kInitAppMsg:
acrxDynamicLinker->unlockApplication(appId);
acrxDynamicLinker->registerAppMDIAware(appId);
acedGetPoint(NULL, "Enter Start Point", startPt);
acedGetPoint(startPt, "Enter Start Point", endPt);
geStartPt[0] = startPt[0];
geStartPt[1] = startPt[1];
geStartPt[2] = startPt[2];
geEndPt[0] = endPt[0];
geEndPt[1] = endPt[1];
geEndPt[2] = endPt[2];
lineId = createLine(geStartPt, geEndPt);
break ;
case AcRx::kUnloadAppMsg:
acutPrintf("\nYour simple ARX application has been unloaded\n");
break ;
}
return AcRx::kRetOK;
}
When the code executes, I get the prompt at command line "Enter start point". But the AutoCAD hangs and I cannot enter the point or do anything else. I feel sure that the function acedGetPoint() is causing the problem. The createLine() function is never called because the AutoCAD hangs before it.
regards,
Jatin.
By
Anonymous, at 1:13 PM
Hello Jatin,
The problem is because you are running an user interaction function from inside kInitAppMsg. According to SDK manual, during this message callback:
"Don't expect device drivers to be initialized, any user interface resources to be active, applications to be loaded in a particular order, AutoLISP to be present, or any databases to be open. Calls involving any of these assumptions will result in an error condition, sometimes fatal. AcDb and AcGi libraries are generally not yet active, although related AcRx and other structures are in place.
"
The right thing to do is to register a command and map this command to a function that will execute your procedures.
Regards,
Fernando.
By
Fernando Malard, at 1:41 PM
Hi,
Thanks very much. But I dont know how to register a command and map it to a function. Can you please tell me the details for the same ?
By
Anonymous, at 7:57 PM
Hi,
You just need to add a new command using the "a>" icon at ARXWizard's toolbar.
Check the Lab1 sample at:
http://arxdummies.blogspot.com/2005/03/lab-1-creating-and-editing-entities.html
Regards,
Fernando.
By
Fernando Malard, at 8:14 PM
Hi Fernando,
Thanks once again. The ARX Wizard installation does'nt have ArxWizards.msi file in the ObjARXWiz folder. There is a file named WizardSetup.exe but running this file does'nt include any ToolBar to the Visual Studio. I am using ObjectARX for AutoCAD 2000. How do I get the ObjectARX Wizard working ?
Jatin.
By
Anonymous, at 6:12 AM
Hi Jatin,
Yes, on 2000 the ARXWizard installer is an EXE file.
It should create a new toolbar (but if I remember correctly it is initally hidden so you have to enable it through a right click at VStudio toolbars).
It should also create a new project type option at VStudio new project dialog "ObjectARX...".
Could you check this?
Regards,
Fernando.
By
Fernando Malard, at 8:50 AM
Hi Fernando,
I have registerd the command through the wizard and its working fine. Thanks for all the help.
regards,
Jatin.
By
Anonymous, at 4:54 PM
I registered my command in aoutocad and from command handler function i am making two consecutive calls to acedcommands.
It fails (with error message out of memory.)on second calls if i follow a specific flow in my application.But for other work flow it forks fine.
I tried out by replacing second call to acedCommands with sendStringToExecute and it works.
I want to know the reason behind this behaviour.
Thanks.
By
Anonymous, at 4:43 AM
Without looking at your code is hard to say what is going on.
It is possible to post your code here or send me through my e-mail?
Try to place the following call to acedCommand, between your two existing command calls, like this:
acedCommand(RTNONE);
Check if this will solve the problem. If not, please give me more details.
Fernando.
By
Fernando Malard, at 8:01 AM
Hello:
You mentioned that "
I tried out by replacing second call to acedCommands with sendStringToExecute and it works."
Is it possible to paste a couple of code to illustrate your method please?
Cheers
By
Anonymous, at 1:20 PM
Hello
How can I make a global variable similar to Autocad system variables, that can be seen from every object in Autocad?
By
Anonymous, at 5:49 AM
Hello,
You may use a custom Object inside NOD (Named Object Dictionary). This object, derived from AcDbObject, can be stored there and may contain everything you want.
Next, create some commands to read/write these object´s properties.
Another approach is to use AcDbXRecord object and attach it to a specific extension dictionary.
In both above solutions your data will persist inside each DWG file.
In fact the best solution will depend on the use this variable will require.
All these options are well explained on ObjectARX samples.
Regards,
Fernando.
By
Fernando Malard, at 8:24 AM
Hi Fernando,
In my application I have to DWG file opened.
I need to read some information from two DWG without change current document.
pdocMgr = acDocManager ;
pAcDoc = acDocManager->document(pDataBase);
acDocManager->activateDocument(pAcDoc);
I use activateDocument to be able to use global function just like
acedSSSetFirst(NULL, NULL);
I can’t use these function to second DWG without using activeDocument
-ActiveDocument take long time when interchangeable between two DWG
Best Regards
M.S
By
Anonymous, at 4:15 AM
Hi M.S,
Every acedXXXXX() like function interact with AutoCAD interface and thus within the current document context.
Every document has its own command heap, memory buffer, AcDbDatabase, etc.
I'm afraid activateDocument() is the only way to go.
Regards,
Fernando.
By
Fernando Malard, at 8:25 AM
Hi Fernando
Thanks a lot
Best Regards,
M.S
By
Anonymous, at 9:26 AM
Dear fernando,
when I use activateDocument fucnction the next line of code does not execute until activate last document.
why? what is the solution???
and may case "Command may be nested more than 4 deep" error??
Do you have any idea???
Regards,
M.S
By
Anonymous, at 3:48 AM
Dear Fernanod,
I have this code
AcDbVoidPtrArray *m_pDBArray = acdbActiveDatabaseArray();
if(m_pDBArray->length() >= 2)
{
AcDbDatabase *pDb1 = (AcDbDatabase *)(m_pDBArray->at(0));
AcDbDatabase *pDb2 = (AcDbDatabase *)(m_pDBArray->at(1));
AcApDocument *pDoc0;
AcApDocManager *doc_manager = acDocManagerPtr();
pDoc0 = doc_manager->curDocument();
AcDbDatabase *pDbTemp0 = pDoc0->database();
if(pDbTemp0 == pDb1)
{
AcApDocument *pDoc;
pDoc = doc_manager->document(pDb2);
Acad::ErrorStatus error11= doc_manager->activateDocument(pDoc);
acutPrintf("\n1");
acutPrintf("\n2");
acutPrintf("\n3");
}
else if(pDbTemp0 == pDb2)
{
AcApDocument *pDoc;
pDoc = doc_manager->document(pDb1);
Acad::ErrorStatus error11= doc_manager->activateDocument(pDoc);
acutPrintf("\n11");
acutPrintf("\n12");
acutPrintf("\n13");
}
}
else
{
acutPrintf("\n Number of DWG less than 2");
}
I have a problem when use this code no acutPrintf execute until reactivate document (manually by click on DWG)
Do you have any idea
Regards,
M.S
By
Anonymous, at 4:20 AM
Hi M.S,
Try to use sendStringToExecute() method. Be aware that there is a stack of commands associated with each document and these commands may not execute at the prompt in time and order you expect.
Take a look at MDI documentation for further information.
Regards,
Fernando.
By
Fernando Malard, at 10:41 AM
Thanks fernando
M.S
By
Anonymous, at 11:55 AM
Hi fernando,
I have one question.
How to disable printing from AutoCAD?
Regards,
M.S
By
Anonymous, at 6:40 AM
Hi M.S,
Could you explain better?
Do you mean disable all printing commands or just some specific entities printing?
Fernando.
By
Fernando Malard, at 8:37 AM
Hi fernando,
I need to disable printing commands
(Disable print command in File Menu)
By
Anonymous, at 9:11 AM
M.S,
During your kLoadDwgMsg you can undefine native AutoCAD commands and then redefine, if you want, with the same name or leave them undefined.
To do that, use the following function to disable print command:
ads_queueexpr( "(command \"_.undefine\" \"print\")" );
You can do the same for any other AutoCAD native command.
Hope this help.
Fernando.
By
Fernando Malard, at 9:31 AM
Hi Fernando,
ads_queueexpr( "(command \"_.undefine\" \"print\")" );
Generat Compile Error :undeclared identifier ads_queueexpr
I use ARX wizard is any Header File to include.
By
Anonymous, at 10:03 AM
Hi M.S,
It is undocumented.
Put this:
extern "C" int ads_queueexpr(const char*);
on the CPP where are your trying to use it.
Regards,
Fernando.
By
Fernando Malard, at 10:15 AM
Hi Fernando,
Yes It Work Thank You very Much
I Have Other Question on print(plot)
I try to add AcplReactotPlot but when I load it to AutoCad I Got
FATAL ERROR: Class 'cstmPlotR' parent named 'AcPlotReactor' not Found
Do you have any idea.
Tanks again
Best Regards,
M.S
By
Anonymous, at 10:44 AM
Hello,
Take a look at this sample:
ObjectARX 2007\samples\editor\AsdkPlotAPI
It shows everything you need.
Regards.
By
Fernando Malard, at 11:00 AM
Hi Fernando,
I have 3 Question.
1) Is any way to rename comand.
Such as rename line command to DrawLine.
2) Is any way to cancel Running Event.
Such as Print Command.
3) Is any way to get event for command
Regards,
M.S
By
Anonymous, at 6:38 AM
Hi M.S,
1)You need to undefine the existing command and add yours with the same name. There is no way to redefine a command.
2)Use AcApDocManagerReactor to handle the docLockModeChanged() event. Inside this method, check you want and call the veto() method if you want to cancel the command.
3)Take a look at AcEdEditorReactor class. It will implement command events such as commandoWillStart(), commandEnded(), etc.
Regards.
By
Fernando Malard, at 10:04 AM
Thank You Frenando
M.S
By
Anonymous, at 10:10 AM
Hy.
Is it possible to get a list of the system variables (or at least those that get saved in the DWG) ?
I'd like to be able to get a list with all variables and then use acedGetVar and acedSetVar using the list, is it possible somehow ?
By
AlbuEmil, at 7:44 AM
Hi AlbuEmil,
I'm afraid you can't.
System variables were implemented through different classes because some are per DWG and some are per AutoCAD instance.
For instance, part of them are inside AcDbDatabase and part inside AcDbAppSystemVariables.
Regards,
By
Fernando Malard, at 8:21 AM
Hello Fernando!
I try to insert a block in the current document (an empty dwg file) If the UCS is World, using acedGetPoint() I have no problem with block positioning. But if I change the UCS position, the insertion point of the block is still related to the WUCS .I do not understand what is wrong, as I've read in documentation about acedGetPoint(..) that "the coordinates of the point stored in result are expressed in terms of the current UCS"... But on my screen is still based on the WUCS!!
Do I have to make a transformation from the current UCS into the World UCS and after the insertion to restore the ucs back to the last user? Is this really necessary?
Any idea?
Kind regards!
Ioan
By
Bordei, at 1:37 PM
Hello Ioan,
acedGetPoint() returns the point on the current UCS:
"The coordinates of the point stored in result are expressed in terms of the current UCS."
Internally ObjectARX deal on almost its methods within WCS. You will need to convert using these functions to go from/to:
bool acdbWcs2Ucs(ads_point p, ads_point q, bool vec);
bool acdbUcs2Wcs(ads_point p, ads_point q, bool vec);
Try to get your point and convert before create the entity.
By
Fernando Malard, at 4:33 PM
Post a Comment
<< Home