Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

ObjectARX : reopen an object which is already open OR how to make software robust

83 views
Skip to first unread message

alfred.hock

unread,
Jul 13, 2000, 3:00:00 AM7/13/00
to
Hallo all,

I have a problem when I was trying to reopen an already opened object. The
problem occurs, when something is going wrong during the calculation of an
arx-function of myself. Than, it is possible that not all objects where
properly closed and the function immediatly returns. I want to solve the
problem in the following manner. The user must call a repair function after
reading the error-message.
But, I cannot reopen the objects, which are already open for writing.
Does anyone know a solution to release the former open-for-write object and
reopen the object in the repair function?
I think a example should work :
Before the user can start to work with my functions, he must call a special
function, which loads predefined layers, linetypes,textstyles .. and so on
from a template drawing. I open a second database object and read all the
information into the current drawing. For this example I assume that a layer
with the same name exists in the current drawing, but the layer is locked. I
don't want to check for locked layers ( in this example ). My function
immediatly returns and send a message to the user, that he has to unlock all
layers and call the repair function. Because I am lazy, I don't worry about
open objects and my functions leaves the objects in an open state.
This example is only a awkward construction, but there are possibilities
that an object is in an open state when my function returns. So I don't want
the user to save the drawing, reload it and continue work with it. I think
it is better to have a repair function, which corrects 'known' und 'unknown'
errors.
Betrand Meyer gives us a definition of

ROBUSTNESS : robustness is the ability of software systems to react
appropriately to abnormal conditions.

My programm lacks of robustness, so I have to make it robust. I can't forsee
every user interaction with AutoCAD. And some AutoCAD functions can destroy
my inserted objects. For instance I have created dynamic blocks which
contain other blocks. When the user inserts new subblocks and don't handle
them correctly some functions will fail. In this case I have to recreate the
former block. Ok, I can attach reactors to the blocks, but not all user have
my application ( external firms ). Those users should only change other
drawing elements. But when they destroy my blocks partly and my customer
want to work with this blocks, errors occur. And this is the point where I
have to write error correction functions.
I hope you unterstand my problem.
My question is therefore : How can I release the open state of an object in
order to reopen the object?

Thanks and best regards

Alfred

Alex Januszkiewicz

unread,
Jul 14, 2000, 3:00:00 AM7/14/00
to
LOL. Amazing.
Now I know where the crowd of DWG Recovery clients is coming from, thank you
for coming out of the closet. You just don't feel like implementing any
error trapping in your "programs" or miss closing object here and there!
To answer your question: if you are too lazy (your own words) to open and
close each object, use transactions. Better yet, use lisp instead of ARX, at
least you will not corrupt your user's drawings, often worth $1000's each.

--
Alex Januszkiewicz
IntelCAD Systems / DWG Data Recovery Services
http://www.intelcad.com
* Standalone (no AutoCAD needed) Attribute Data Extraction program *
* Standalone Xref Re-path program *
* DWF => DWG converter*
--

alfred.hock <alfre...@debitel.net> wrote in message
news:396d...@news.ivm.net...

alfred.hock

unread,
Jul 14, 2000, 3:00:00 AM7/14/00
to
Hello Alex,

I think you are little bit unfair. You cannot always check any abnormal
conditions. I'll give you an example of having no chance to close your
objects properly.
Assume some other application has inserted a AcDbBlockReference and the
ObjectId of the AcDbBlockTableRecord isn't valid (This is the actual
error ). This would cause the "undefined block#-1" message. If the user
ignores this message when he is loading the drawing he can insert lines,
circles and so on. All the user input work. But then he calls a function you
have defined.

void yourFunction()
{
.....
actrTransactionManager->startTransaction();
try{
// open objects, In this example : no error checking ,
// In the real world there is no chance to
avoid error checking
acdbCurDwg()->getBlockTable(pBlockTable,AcDb::kForRead);
pBlockTable->getAt(*MODEL_SPACE,pMSB,AcDb::kForRead);
pBlockTable->close();

.....
// You want to load all blocks which are prefixed with BART_SIMPSON
struct resbuf rbb;
strcpy(strBuf,"BART_SIMPSON*");
rbb.restype = 2;
rbb.resval.rstring = strBuf;
rbb.rbnext = NULL;

//
// THE NEXT CALL FAILS --> YOU HAVE NO CHANCE TO CLOSE THE MODEL
SPACE
//
Res = ads_ssget("X", NULL, NULL, &rbb, tempset);
if ( Res != RTNORM ){
...


actrTransactionManager->endTransaction();
}
catch( .... ){
......
actrTransactionManager->abortTransaction();
......
}
}

The ads_ssget function fails and a !smio.cpp xyz error dialog tells the user
that something went wrong. Your function cannot close the block table
object. This is exactly what I mean with abnormal error conditions. This is
also an awkward programm construction. But there are many other examples
when something is going wrong and your application seems to be responsible
for the catastrophe.

Have you any idea how to close the model space?
Now you have doubled the mess in the drawing. You have a corrupted block and
you have no chance to open the model space for writing.
How can I now repair the drawing with only a simple function call? Having
only a writeable model space ....

Thanks and best regards,

Alfred.

BTW : A great deal of my work is dedicated on error checking.


Owen Wengerd

unread,
Jul 14, 2000, 3:00:00 AM7/14/00
to
Alfred:

> // THE NEXT CALL FAILS --> YOU HAVE NO CHANCE TO
> CLOSE THE MODEL SPACE

You have several ways of closing the model space block. You could end the
transaction, but the best way is to open it without using the transaction
model, then close it immediately when you're done (and *before* calling
ads_ssget).

There is no case where you cannot encapsulate your object access in such a
way as to ensure that all your open objects are closed before you exit the
encapsulated scope, and that is really the only solution to this dilemma. :)
--
Owen Wengerd
President, ManuSoft ==> http://www.manusoft.com
VP Americas, CADLock, Inc. ==> http://www.cadlock.com

Byron Blattel

unread,
Jul 14, 2000, 3:00:00 AM7/14/00
to
I'm recently started using some 'smart' pointers for just this purpose. When
they go out of scope they close the pointer they wrap.

For example:

template <class T> class CxPtr
{
public:
T* ptr;
CxPtr() { ptr = NULL; }
~CxPtr(void) { }
operator T*(void) { return ptr; }
T& operator*(void) { return *ptr; }
T* operator->(void) { return ptr; }
CxPtr& operator=(CxPtr<T> &ptr_) {return operator=((T *) ptr_);}
CxPtr& operator=(T* ptr_) { return *this; }
};

class AcDbBlockTablePtr : public CxPtr<AcDbBlockTable>
{
public:
AcDbBlockTablePtr(AcDb::OpenMode openmode = AcDb::kForRead, AcDbDatabase*
database = NULL);
virtual ~AcDbBlockTablePtr();
protected:
AcDb::OpenMode m_OpenMode;
};

// AcDbBlockTable smart pointer
AcDbBlockTablePtr::AcDbBlockTablePtr(AcDb::OpenMode openmode, AcDbDatabase*
database)
{
m_OpenMode = openmode;
Acad::ErrorStatus result;
if(database)
#ifdef R14
result = database->getBlockTable(ptr, m_OpenMode);
else
result = acdbCurDwg()->getBlockTable(ptr, m_OpenMode);
#else
result = database->getSymbolTable(ptr, m_OpenMode);
else
result = acdbHostApplicationServices()->workingDatabase()->getSymbolTable(ptr,
m_OpenMode);
#endif
if(result != Acad::eOk) throw result;
}

AcDbBlockTablePtr::~AcDbBlockTablePtr()
{
if(ptr)
ptr->close();
}

Comments welcome :)

--
|
----+----------------------------------------------
| Byron Blattel
| CADwerx---Applications for AutoCAD
|
| by...@cadwerx.net
| http://www.cadwerx.net
|

Byron Blattel

unread,
Jul 14, 2000, 3:00:00 AM7/14/00
to
Owen Wengerd wrote:
>
> Byron:
>
> > Comments welcome :)
>
> Looks OK, but a little more complex than I like. I tend to custom design
> them for each project, as I don't like the one size fits all approach. BTW,

I think we're actually in agreement here. In my case the template is just for
the overloaded pointer operators.

> Markus Kraus of the ADN support group has designed a do-it-all smart pointer
> which even does reference counting -- it used to be on the ADN CDs, and
> might be in the current ObjectARX SDK, if anyone's interested.

I'll look for it. Maybe it's just my architectural background, but I find most
of the Autodesk sample code quite offensive to read and often much more
convoluted then it need be. I hope they put more effort into the production
code :(

> Here's a sample from one of my source files, probably munged horribly by
> word wrapping:
>
> template <class TYPE> class SmartOpenObjectPtr
> {
> protected:
> AcDbObject* m_pObject;
> public:
> SmartOpenObjectPtr( TYPE* pObject = NULL ) { m_pObject = pObject; }
> ~SmartOpenObjectPtr() { if( m_pObject ) m_pObject->close(); }
> Acad::ErrorStatus close() { Acad::ErrorStatus eStat = m_pObject?
> m_pObject->close() : Acad::eOk; m_pObject = NULL; return eStat; }
> Acad::ErrorStatus cancel() { Acad::ErrorStatus eStat = m_pObject?
> m_pObject->cancel() : Acad::eOk; m_pObject = NULL; return eStat; }
> operator TYPE* () const { return (TYPE*)m_pObject; }
> operator TYPE*& () { return (TYPE*&)m_pObject; }
> TYPE*& operator= ( TYPE* pSrc ) { if( m_pObject ) m_pObject->close();
> m_pObject = pSrc; return (TYPE*&)m_pObject; }
> TYPE& operator* () const { return *(TYPE*)m_pObject; }
> TYPE* operator-> () const { return (TYPE*)m_pObject; }
> };
>
> You declare a smart pointer in code like so:
> SmartOpenObjectPtr< AcDbBlockTableRecord > pBTR;

I considered using it this way, but I decided I would make them look more like a
regular class and incorporate some added functionality. For one thing I can't
stand iterators, but I really like vectors so I've got a:

vector<CString> AcDbBlockTablePtr::GetNames(bool blocks, bool layouts, bool
xrefs, bool anonymous);
vector<AcDbObjectId> GetIds(bool blocks = true, bool layouts = true, bool xrefs
= true, bool anonymous = true);

So I can make a vector of names or ids and keep it around if need be. I can
then use the ids in a simple for() loop with a local smart record pointer to
open them up read or write. Makes the code a lot easier to follow.

> pBTR then acts just like a normal AcDbBlockTableRecord*. Whenever the
> smart pointer goes out of scope by any means, the AcDbBlockTableRecord it
> points to is closed.
>
> P.S. The usage instructions are for the peanut gallery, not you Byron. I
> don't want to insult your intelligence. :)

Ha, don't over estimate it either :) BTW, what are 'RAII objects'?

Owen Wengerd

unread,
Jul 15, 2000, 3:00:00 AM7/15/00
to
Byron:

> Comments welcome :)

Looks OK, but a little more complex than I like. I tend to custom design
them for each project, as I don't like the one size fits all approach. BTW,

Markus Kraus of the ADN support group has designed a do-it-all smart pointer
which even does reference counting -- it used to be on the ADN CDs, and
might be in the current ObjectARX SDK, if anyone's interested.

Here's a sample from one of my source files, probably munged horribly by
word wrapping:

template <class TYPE> class SmartOpenObjectPtr
{
protected:
AcDbObject* m_pObject;
public:
SmartOpenObjectPtr( TYPE* pObject = NULL ) { m_pObject = pObject; }
~SmartOpenObjectPtr() { if( m_pObject ) m_pObject->close(); }
Acad::ErrorStatus close() { Acad::ErrorStatus eStat = m_pObject?
m_pObject->close() : Acad::eOk; m_pObject = NULL; return eStat; }
Acad::ErrorStatus cancel() { Acad::ErrorStatus eStat = m_pObject?
m_pObject->cancel() : Acad::eOk; m_pObject = NULL; return eStat; }
operator TYPE* () const { return (TYPE*)m_pObject; }
operator TYPE*& () { return (TYPE*&)m_pObject; }
TYPE*& operator= ( TYPE* pSrc ) { if( m_pObject ) m_pObject->close();
m_pObject = pSrc; return (TYPE*&)m_pObject; }
TYPE& operator* () const { return *(TYPE*)m_pObject; }
TYPE* operator-> () const { return (TYPE*)m_pObject; }
};

You declare a smart pointer in code like so:
SmartOpenObjectPtr< AcDbBlockTableRecord > pBTR;

pBTR then acts just like a normal AcDbBlockTableRecord*. Whenever the


smart pointer goes out of scope by any means, the AcDbBlockTableRecord it
points to is closed.

P.S. The usage instructions are for the peanut gallery, not you Byron. I
don't want to insult your intelligence. :)

Owen Wengerd

unread,
Jul 15, 2000, 3:00:00 AM7/15/00
to
Byron:

> what are 'RAII objects'?

RAII = "Resource Acquisition Is Initialization", a phrase coined to refer
to a class whose sole purpose is to release a resource upon destruction.
Over in comp.lang.c++.moderated there's been a long debate about a better
name and a more precise definition. :)

0 new messages