How to use AcDbEvalGraph::getAllNodes()

How to use AcDbEvalGraph::getAllNodes()

smx_khang
Contributor Contributor
868 Views
8 Replies
Message 1 of 9

How to use AcDbEvalGraph::getAllNodes()

smx_khang
Contributor
Contributor

Hello everyone

I'm working with AcDbEvalGraph, my task is get and print number of node in a graph. 

 

I maked a function as below, that can successully build and load into AutoCAD. But when I run it, I faced an error.

In debug mode, VS said: "A breakpoint instruction (__debugbreak() statement or a similar call) was executed in acad.exe"

Why that error occurs? Did I have any misstake?

 

 

 

void test() {
    Acad::ErrorStatus es;
    AcDbObjectId objId;

    ads_name name; ads_point p;
    if (acedEntSel(_T("\nSelect block reference: "), name, p) != RTNORM)
        return;

    acdbGetObjectId(objId, name);
    AcDbBlockReference* pBlkRef;
    es = acdbOpenObject(pBlkRef, objId, AcDb::kForRead, false);
    if (es != Acad::eOk) return;

    AcDbDynBlockReference dynBlk(objId);
    AcDbBlockTableRecordPointer pBTR(dynBlk.dynamicBlockTableRecord(), AcDb::kForRead);
    if (pBTR.openStatus() != Acad::eOk) return;

    // Open Dictionary of Block Table Record
    objId = pBTR->extensionDictionary();
    AcDbDictionary* pDic;
    es = acdbOpenObject(pDic, objId, AcDb::kForWrite, false);
    if (es != Acad::eOk) return;

    // Open EvalGraph
    es = pDic->getAt(_T("ACAD_ENHANCEDBLOCK"), objId);
    if (es != Acad::eOk) return;
    AcDbEvalGraph* graph;
    es = acdbOpenObject(graph, objId, AcDb::kForWrite, false);
    if (es != Acad::eOk) return;

    // Get length of NodeIdArray
    AcDbEvalNodeIdArray nodes;
    es = graph->getAllNodes(nodes);
    if (es != Acad::eOk) return;
    acutPrintf(_T("\nLength: %d", nodes.length()));
}

 

 

 

I'm using AutoCAD 2023.

Visual Studio 2022 with CPP project is not generated by ObjectARX Wizard.

0 Likes
869 Views
8 Replies
Replies (8)
Message 2 of 9

tbrammer
Advisor
Advisor

You have wrong braces in acutPrintf(_T("\nLength: %d", nodes.length()));  
It should be acutPrintf( _T("\nLength: %d"), nodes.length() );
I tested with this fix and it worked fine on AutoCAD 2022.

If you still get problems try to open pDic and graph kForRead only instead of kForWrite.

 


Thomas Brammer ● Software Developer ● imos AGLinkedIn
If an answer solves your problem please [ACCEPT SOLUTION]. Otherwise explain why not.

0 Likes
Message 3 of 9

smx_khang
Contributor
Contributor

@tbrammer  Thanks for your answer. And sorry for my late reply.
I checked the syntax of the program again, I also tried opening the objects to read-only, but the problem is not solved. (That error arises when the system tries to delete the arr object).
Then I did some research and found out that the problem seems to come from the way the array object is declared.
Just do this, everything works fine

// Get length of NodeIdArray
AcDbEvalNodeIdArray* nodes = new AcDbEvalNodeIdArray();
es = graph->getAllNodes(*nodes);

 

0 Likes
Message 4 of 9

tbrammer
Advisor
Advisor

In AutoCAD 2024 I don't get any exceptions with the original test code lines:

AcDbEvalNodeIdArray nodes;
es = graph->getAllNodes(nodes);

The call itself should be safe with an AcDbEvalNodeIdArray on the stack.

You wrote: "That error arises when the system tries to delete the arr object"

Do you mean that it arises, when nodes goes out of scope?

Or did you pass the pointer &nodes to another function that calls delete for the pointer?

 


Thomas Brammer ● Software Developer ● imos AGLinkedIn
If an answer solves your problem please [ACCEPT SOLUTION]. Otherwise explain why not.

0 Likes
Message 5 of 9

smx_khang
Contributor
Contributor
Yes, I meant "when it goes out of scope".
I have set breakpoint and next line by line, I can safely go through line 35 (in the original code), but the program stopped at the last line, line 36, the line has only closing parenthesis.
In the AutoCAD's command line, the number of nodes was also printed.
0 Likes
Message 6 of 9

daniel_cadext
Advisor
Advisor

IMHO, you should be consistent with using smart pointers, so you know everything is closed if there’s an early return

Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx
0 Likes
Message 7 of 9

tbrammer
Advisor
Advisor

Probably a silly question: Did you delete the array without problems?

AcDbEvalNodeIdArray* nodes = new AcDbEvalNodeIdArray();
es = graph->getAllNodes(*nodes);
// print nodes ...
delete nodes;

I really can't imagine why it should make a difference to use a AcDbEvalNodeArray on the stack or an array on the heap created with new - unless there are other general stack memory problems - like a wrong acutPrintf().

Do you also get the exception if you register your function void test() with acutPrintf()fix and  nodes on stack as command and execute it?


Thomas Brammer ● Software Developer ● imos AGLinkedIn
If an answer solves your problem please [ACCEPT SOLUTION]. Otherwise explain why not.

0 Likes
Message 8 of 9

smx_khang
Contributor
Contributor
I was a bit confused.
It's embarrassed to say that I forgot to delete the pointer nodes.
As you said, there is no difference between using pointers and not. When I deleted the pointer using delete operator, the same error happened (Even if I commented-out acutPrintf)
I also registered my test() as a command and executed it.
0 Likes
Message 9 of 9

tbrammer
Advisor
Advisor

Although @daniel_cadext already gave the advice to use smart pointers consistently I didn't realize that you forgot to close() several objects. Please try with the fixes commented " //***TB  ":

void test() {
    Acad::ErrorStatus es;
    AcDbObjectId objId;

    ads_name name; ads_point p;
    if (acedEntSel(_T("\nSelect block reference: "), name, p) != RTNORM)
        return;

    acdbGetObjectId(objId, name);
    AcDbBlockReference* pBlkRef;
    es = acdbOpenObject(pBlkRef, objId, AcDb::kForRead, false);
    if (es != Acad::eOk) 
		return;
	pBlkRef->close();	// ***TB Don't forget to close pBlkRef!!!

    AcDbDynBlockReference dynBlk(objId);
    AcDbBlockTableRecordPointer pBTR(dynBlk.dynamicBlockTableRecord(), AcDb::kForRead);
    if (pBTR.openStatus() != Acad::eOk) 
		return;

    // Open Dictionary of Block Table Record
    objId = pBTR->extensionDictionary();
    AcDbDictionary* pDic;
    es = acdbOpenObject(pDic, objId, AcDb::kForRead, false); //  ***TB (kForRead is enough)
    if (es != Acad::eOk) 
		return;

    // Open EvalGraph
    es = pDic->getAt(_T("ACAD_ENHANCEDBLOCK"), objId);
	pDic->close();			// ***TB Don't forget to close pDic!!!
    if (es != Acad::eOk) 
		return;

    AcDbEvalGraph* graph;
    es = acdbOpenObject(graph, objId, AcDb::kForRead, false);  //  ***TB (kForRead is enough)
    if (es != Acad::eOk) 
		return;

    // Get length of NodeIdArray
    AcDbEvalNodeIdArray nodes;
    es = graph->getAllNodes(nodes);
	graph->close();			// ***TB Don't forget to close graph!!!
	
    if (es != Acad::eOk) 
		return;
    acutPrintf(_T("\nLength: %d"), nodes.length()); // ***TB braces fixed
}

 

Using smart pointers avoid the danger to forget to close open objects. They automatically close the object as soon as they go out of scope. Instead of writing

es = acdbOpenObject(pBlkRef, objId, AcDb::kForRead, false);

you can write

AcDbObjectPointer<AcDbBlockReference> pBlkRef(objId, AcDb::kForRead, false); 
es = pBlkRef.openStatus();

AcDbObjectPointer<T> have an overloaded operator->() so that you can use it exactly like a normal pointer.

 


Thomas Brammer ● Software Developer ● imos AGLinkedIn
If an answer solves your problem please [ACCEPT SOLUTION]. Otherwise explain why not.

0 Likes