Author Topic: Deriving from AcDbPolyLine  (Read 7012 times)

0 Members and 1 Guest are viewing this topic.

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6935
  • AKA Daniel
Deriving from AcDbPolyLine
« on: March 24, 2009, 01:45:15 AM »
I have a class that derives from AcDbployLine, (Not a custom Object)  after I add a few of these objects to the database then try to unload my ARX app, Acad crashes. 
Am I doing something wrong?

code I am using to add the entity
Code: [Select]
   AcDbObjectId pid;
   AcDbPolyline *p = new CrpRectangle(AcGePoint2d(0,0),400,400,PCode::kUpperRight);

   //closes the entity
   if (DatabaseTools::AddToCurrentSpace(p,pid) == Acad::eOk)
     acutPrintf(_T("\nok")
);


.h
Code: [Select]
#pragma once

#include "TypedValue.h"
#include "pcode.h"

#define ISEOK(statement)  { Acad::ErrorStatus st = (statement); if (st != Acad::eOk) return st; }

class CrpRectangle: public AcDbPolyline
{
public:
  CrpRectangle(DataMap &tvmap);
  CrpRectangle(const AcGePoint2d &pt, double width, double height, PCode::Location loc);
  virtual ~CrpRectangle(void);

public:
  Acad::ErrorStatus getWidth(double &width) const;
  Acad::ErrorStatus setWidth(double width);
  Acad::ErrorStatus getHeight(double &height) const;
  Acad::ErrorStatus setHeight(double height);
  Acad::ErrorStatus drawFrom(const AcGePoint3d &point);
  Acad::ErrorStatus drawFrom(const AcGePoint2d &point);
  Acad::ErrorStatus drawFrom(const AcGeVector2d &vec);
private:
  void calcLocation(const AcGePoint2d &startPoint, double width, double height, PCode::Location loc);

public:
 AcGePoint2d getPt1() const;
 AcGePoint2d getPt2() const;
 AcGePoint2d getPt3() const;
 AcGePoint2d getPt4() const;
 AcGePoint2d getMPB() const;
 AcGePoint2d getMPR() const;
 AcGePoint2d getMPT() const;
 AcGePoint2d getMPL() const;
 AcGePoint2d getCenter() const;

__declspec(property(get = getPt1))AcGePoint2d PT1;
__declspec(property(get = getPt2))AcGePoint2d PT2;
__declspec(property(get = getPt3))AcGePoint2d PT3;
__declspec(property(get = getPt4))AcGePoint2d PT4;
__declspec(property(get = getMPB))AcGePoint2d MPB;
__declspec(property(get = getMPR))AcGePoint2d MPR;
__declspec(property(get = getMPT))AcGePoint2d MPT;
__declspec(property(get = getMPL))AcGePoint2d MPL;
__declspec(property(get = getCenter))AcGePoint2d Center;
};

.cpp
Code: [Select]
#include "StdAfx.h"
#include "CrpRectangle.h"

CrpRectangle::CrpRectangle(DataMap &tvmap):AcDbPolyline(4)
{
  AcGePoint2d pt;
  double width;
  double height;
  short location;

  tvmap[1000].getPoint2d(pt);
  tvmap[1001].getDouble(width);
  tvmap[1002].getDouble(height);
  tvmap[1003].getShort(location);

  AcGePoint2d pt1 (pt.x + width,pt.y);
  AcGePoint2d pt2 (pt.x + width, pt.y + height);
  AcGePoint2d pt3 (pt.x, pt.y + height);
  if(this->isWriteEnabled())
  {
    this->addVertexAt(0,pt);
    this->addVertexAt(1,pt1);
    this->addVertexAt(2,pt2);
    this->addVertexAt(3,pt3);
    this->setClosed(Adesk::kTrue);
  }
  calcLocation(pt, width, height, (PCode::Location)location);
}

CrpRectangle::CrpRectangle(const AcGePoint2d &pt, double width, double height, PCode::Location loc):AcDbPolyline(4)
{
  AcGePoint2d pt1 (pt.x + width,pt.y);
  AcGePoint2d pt2 (pt.x + width, pt.y + height);
  AcGePoint2d pt3 (pt.x, pt.y + height);
  if(this->isWriteEnabled())
  {
    this->addVertexAt(0,pt);
    this->addVertexAt(1,pt1);
    this->addVertexAt(2,pt2);
    this->addVertexAt(3,pt3);
    this->setClosed(Adesk::kTrue);
  }
  calcLocation(pt, width, height, loc);
}

CrpRectangle::~CrpRectangle(void)
{
}

Acad::ErrorStatus CrpRectangle::getWidth(double &width) const
{
  Acad::ErrorStatus res;
  AcGePoint2d p0;
  AcGePoint2d p1;
  if((res = this->getPointAt(0,p0)) != Acad::eOk)
    return res;
  if((res = this->getPointAt(1,p1)) != Acad::eOk)
    return res;
  width = fabs(p0.x - p1.x);
  return Acad::eOk;
}

Acad::ErrorStatus CrpRectangle::setWidth(double width)
{
  Acad::ErrorStatus res;
  AcGePoint2d p0;
  AcGePoint2d p1;
  AcGePoint2d p2;

  if((res = this->getPointAt(0,p0)) != Acad::eOk)
    return res;

  if((res = this->getPointAt(1,p1)) != Acad::eOk)
    return res;

  if((res = this->getPointAt(2,p2)) != Acad::eOk)
    return res;

  p1.x = p0.x + width;
  p2.x = p0.x + width;

  if(!this->isWriteEnabled())
    return Acad::eNotOpenForWrite;

  if((res = this->setPointAt(1,p1)) != Acad::eOk)
    return res;

  if((res = this->setPointAt(2,p2)) != Acad::eOk)
    return res;

  return Acad::eOk;
}

Acad::ErrorStatus CrpRectangle::getHeight(double &height) const
{
  Acad::ErrorStatus res;
  AcGePoint2d p0;
  AcGePoint2d p4;
  if((res = this->getPointAt(0,p0)) != Acad::eOk)
    return res;
  if((res = this->getPointAt(4,p4)) != Acad::eOk)
    return res;
  height = fabs(p4.y - p0.y);
  return Acad::eOk;
}

Acad::ErrorStatus CrpRectangle::setHeight(double height)
{
  Acad::ErrorStatus res;
  AcGePoint2d p0;
  AcGePoint2d p2;
  AcGePoint2d p3;

  if((res = this->getPointAt(0,p0)) != Acad::eOk)
    return res;

  if((res = this->getPointAt(2,p2)) != Acad::eOk)
    return res;

  if((res = this->getPointAt(3,p3)) != Acad::eOk)
    return res;

  p2.y = p0.y + height;
  p3.y = p0.y + height;

  if(!this->isWriteEnabled())
    return Acad::eNotOpenForWrite;

  if((res = this->setPointAt(2,p2)) != Acad::eOk)
    return res;

  if((res = this->setPointAt(3,p3)) != Acad::eOk)
    return res;

  return Acad::eOk;
}

void CrpRectangle::calcLocation( const AcGePoint2d &startPoint, double width,
                                 double height, PCode::Location loc )
{
  AcGePoint2d point;
  switch ((int)loc)
  {
  case (int)PCode::kLowerLeft:
    {
      point = startPoint;
    }
    break;
  case (int)PCode::kLowerMid:
    {
      point = AcGePoint2d((startPoint[0] - (width / 2)), startPoint[1]);
    }
    break;
  case (int)PCode::kLowerRight:
    {
      point = AcGePoint2d((startPoint[0] - width), startPoint[1]);
    }
    break;
  case (int)PCode::kRightMid:
    {
      point = AcGePoint2d((startPoint[0] - width), (startPoint[1] - (height / 2)));
    }
    break;
  case (int)PCode::kUpperRight:
    {
      point = AcGePoint2d((startPoint[0] - width), (startPoint[1] - height));
    }
    break;
  case (int)PCode::kUpperMid:
    {
      point = AcGePoint2d((startPoint[0] - (width / 2)), (startPoint[1] - height));
    }
    break;
  case (int)PCode::kUpperLeft:
    {
      point = AcGePoint2d(startPoint[0], (startPoint[1] - height));
    }
    break;
  case (int)PCode::kLeftMid:
    {
      point = AcGePoint2d(startPoint[0], (startPoint[1] - (height / 2)));
    }
    break;
  }
  drawFrom(point);
}

Acad::ErrorStatus CrpRectangle::drawFrom( const AcGePoint3d &point )
{
  return drawFrom(AcGePoint2d(point.x,point.y));
}

Acad::ErrorStatus CrpRectangle::drawFrom( const AcGePoint2d &point )
{
  return drawFrom(point.asVector());
}

Acad::ErrorStatus CrpRectangle::drawFrom( const AcGeVector2d &vec )
{
  Acad::ErrorStatus res;

  if(!this->isWriteEnabled())
    return Acad::eNotOpenForWrite;

  AcGePoint2d pt0;
  AcGePoint2d pt1;
  AcGePoint2d pt2;
  AcGePoint2d pt3;

 ISEOK(this->getPointAt(0,pt0));
 ISEOK(this->getPointAt(1,pt1));
 ISEOK(this->getPointAt(2,pt2));
 ISEOK(this->getPointAt(3,pt3));
 ISEOK(this->setPointAt(0, pt0 + vec));
 ISEOK(this->setPointAt(1, pt1 + vec));
 ISEOK(this->setPointAt(2, pt2 + vec));
 ISEOK(this->setPointAt(3, pt3 + vec));

 return Acad::eOk;
}

AcGePoint2d CrpRectangle::getPt1(void) const
{
  AcGePoint2d pt;
  this->getPointAt(0,pt);
  return pt;
}

AcGePoint2d CrpRectangle::getPt2(void) const
{
  AcGePoint2d pt;
  this->getPointAt(1,pt);
  return pt;
}

AcGePoint2d CrpRectangle::getPt3(void) const
{
  AcGePoint2d pt;
  this->getPointAt(2,pt);
  return pt;
}

AcGePoint2d CrpRectangle::getPt4(void) const
{
  AcGePoint2d pt;
  this->getPointAt(3,pt);
  return pt;
}

AcGePoint2d CrpRectangle::getMPB() const
{
  AcGePoint2d pt0;
  this->getPointAt(0,pt0);
  AcGePoint2d pt1;
  this->getPointAt(1,pt1);
  return AcGePoint2d((pt1[0] - pt0[0]) / 2.0, pt0[1]);
}

AcGePoint2d CrpRectangle::getMPL() const
{
  AcGePoint2d pt1;
  this->getPointAt(0,pt1);
  AcGePoint2d pt4;
  this->getPointAt(3,pt4);
  return AcGePoint2d(pt1[0], (pt4[1] - pt1[1]) / 2.0);
}

AcGePoint2d CrpRectangle::getMPR() const
{
  AcGePoint2d pt2;
  this->getPointAt(1,pt2);
  AcGePoint2d pt3;
  this->getPointAt(2,pt3);
  return AcGePoint2d(pt2[0], (pt3[1] - pt2[1]) / 2.0);
}

AcGePoint2d CrpRectangle::getMPT() const
{
  AcGePoint2d pt3;
  this->getPointAt(2,pt3);
  AcGePoint2d pt4;
  this->getPointAt(3,pt4);
  return AcGePoint2d((pt3[0] - pt4[0]) / 2.0, pt3[1]);
}

AcGePoint2d CrpRectangle::getCenter() const
{
  AcGePoint2d pt1;
  this->getPointAt(0,pt1);
  AcGePoint2d pt2;
  this->getPointAt(1,pt2);
  AcGePoint2d pt4;
  this->getPointAt(3,pt4);
  return AcGePoint2d((pt2[0] - pt1[0]) / 2.0, (pt4[1] - pt1[1]) / 2.0);
}

added the arx for 07 (command is test)

pkohut

  • Guest
Re: Deriving from AcDbPolyLine
« Reply #1 on: March 24, 2009, 03:26:31 AM »
I have a class that derives from AcDbployLine, (Not a custom Object)  after I add a few of these objects to the database then try to unload my ARX app, Acad crashes. 
Am I doing something wrong?

The derived class is a custom object, it just happens that it doesn't override any of the
required virtual functions of a normal custom object.  So AcDbPolyline is doing all the
drawing, filing, selecting, etc.  Still the object is of type CrpRectangle and when
Autocad asks the object for its RTTI it's getting CrpRectangle which as long as your
application is loaded then there is a VTable entry for CrpRectangle's code.  Unload
your application, the object is still of type CrpRectangle, but the VTable entry is
no longer valid and causing the crash.

Only when the drawing is saved and reload is your CrpRectangle now truely an
AcDbPolyline object.

Paul

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6935
  • AKA Daniel
Re: Deriving from AcDbPolyLine
« Reply #2 on: March 24, 2009, 03:39:16 AM »
Ok that makes sense, I guess I should probably learn how to make a custom object the correct way , or redo my class so it creates a  polyline.

Thanks  :-)

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6935
  • AKA Daniel
Re: Deriving from AcDbPolyLine
« Reply #3 on: March 24, 2009, 03:53:14 AM »
I could always lock the application via acrxDynamicLinker->lockApplication();   :evil:

MickD

  • Gator
  • Posts: 3300
  • (x-in)->[process]->(y-out)
Re: Deriving from AcDbPolyLine
« Reply #4 on: March 24, 2009, 05:01:36 AM »
Pretty much what Paul said with the main point being that you can add objects to the database (as they derive from an AcDbObject therefore they pass the 'isA' test) but when the db fileout method is called it will crash as there is no function to handle the call.

For your Polyline object you simply call it's fileout() function in your custom fileout function and it will look after itself but for any custom data you will need to write the file out/in functions so it gets filed properly. Generally this isn't too hard as most data is doubles or text which you associate with an inbuilt db object to give it some 'intelligence' or custom modification or grip/snap behaviour, you just have to remember to reverse the order of filein and fileout operations if IRC.

All of this should be handled in a dbx dll with your custom behaviour that doesn't get filed out in an arx, of course your behavioural methods are in the dbx but the commands to make them happen reside in an arx typically.
hth.
Forth is like the Tao: it is a Way, and is realized when followed.
Its fragility is its strength; its simplicity is its direction - Michael Ham

Lao Tzu: ďTo attain knowledge, add things
every day; to obtain wisdom, remove things every day.Ē

MickD

  • Gator
  • Posts: 3300
  • (x-in)->[process]->(y-out)
Re: Deriving from AcDbPolyLine
« Reply #5 on: March 24, 2009, 05:04:09 AM »
There's a section in the doc's which covers the bare minimum requirements for filing objects and how to do it which isn't that hard, the hardest bit is the behavioural side of things (making them reliable/bugproof) IMO.
Forth is like the Tao: it is a Way, and is realized when followed.
Its fragility is its strength; its simplicity is its direction - Michael Ham

Lao Tzu: ďTo attain knowledge, add things
every day; to obtain wisdom, remove things every day.Ē

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6935
  • AKA Daniel
Re: Deriving from AcDbPolyLine
« Reply #6 on: March 24, 2009, 05:23:43 AM »
Thanks Mick,

Iíll to have a look at the docs on building custom objects,  I think there is a sample video out there as well.
I donít need to change the way the entity behaves, I just want to add my own way of constructing the object.
Locking the application seems to prevent AutoCAD from crashing, although it's probably a kludge  :|

Spike Wilbury

  • Guest
Re: Deriving from AcDbPolyLine
« Reply #7 on: March 24, 2009, 01:14:47 PM »
Also.... (and maybe this will help)

When you derive from AcDbPolyline (I have used for some of my custom objects) some of the methods will required the use of 'AcDbCurve' methods, in order to work.

MickD

  • Gator
  • Posts: 3300
  • (x-in)->[process]->(y-out)
Re: Deriving from AcDbPolyLine
« Reply #8 on: March 24, 2009, 03:25:38 PM »
...Locking the application seems to prevent AutoCAD from crashing, although it's probably a kludge  :|


Sorry Dan, I misunderstood you first question (I was thinking unloading the dwg), locking the app my stop it crashing but there's still a problem as you say.
Unloading the app doesn't file the object in/out so what happens when you try to save the file? If you want your object to be saved and read back in you will need to write the filer code, if not you'll probably have to destroy the objects before saving but that's also a bit of a kludge.
Forth is like the Tao: it is a Way, and is realized when followed.
Its fragility is its strength; its simplicity is its direction - Michael Ham

Lao Tzu: ďTo attain knowledge, add things
every day; to obtain wisdom, remove things every day.Ē

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6935
  • AKA Daniel
Re: Deriving from AcDbPolyLine
« Reply #9 on: March 24, 2009, 09:52:56 PM »
After additional testing, it seems everything works just dandy as long as the Arx stays loaded in the session where the object is created.
Saving, Closing, etc. all work. As Paul said, itís probably something to do with the VTable.

Spike Wilbury

  • Guest
Re: Deriving from AcDbPolyLine
« Reply #10 on: March 24, 2009, 11:11:11 PM »
After additional testing, it seems everything works just dandy as long as the Arx stays loaded in the session where the object is created.
Saving, Closing, etc. all work. As Paul said, itís probably something to do with the VTable.

There is a note every time you create a custom object added from the arxwizard.... for example:

Quote
#pragma once

#ifdef EZONEDBX_MODULE
#define DLLIMPEXP __declspec(dllexport)
#else
//----- Note: we don't use __declspec(dllimport) here, because of the
//----- "local vtable" problem with msvc. If you use __declspec(dllimport),
//----- then, when a client dll does a new on the class, the object's
//----- vtable pointer points to a vtable allocated in that client
//----- dll. If the client dll then passes the object to another dll,
//----- and the client dll is then unloaded, the vtable becomes invalid
//----- and any virtual calls on the object will access invalid memory.
//-----
//----- By not using __declspec(dllimport), we guarantee that the
//----- vtable is allocated in the server dll during the ctor and the
//----- client dll does not overwrite the vtable pointer after calling
//----- the ctor. And, since we expect the server dll to remain in
//----- memory indefinitely, there is no problem with vtables unexpectedly
//----- going away.
#define DLLIMPEXP
#endif

MickD

  • Gator
  • Posts: 3300
  • (x-in)->[process]->(y-out)
Re: Deriving from AcDbPolyLine
« Reply #11 on: March 25, 2009, 12:43:35 AM »
It looks like Luis has found a good clue!


After additional testing, it seems everything works just dandy as long as the Arx stays loaded in the session where the object is created.
Saving, Closing, etc. all work. As Paul said, itís probably something to do with the VTable.

hmm, it must be calling the derived class to file out the polyline, does it save your member data as well?
If so that's pretty spooky :)
Forth is like the Tao: it is a Way, and is realized when followed.
Its fragility is its strength; its simplicity is its direction - Michael Ham

Lao Tzu: ďTo attain knowledge, add things
every day; to obtain wisdom, remove things every day.Ē

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6935
  • AKA Daniel
Re: Deriving from AcDbPolyLine
« Reply #12 on: March 25, 2009, 01:10:18 AM »
After additional testing, it seems everything works just dandy as long as the Arx stays loaded in the session where the object is created.
Saving, Closing, etc. all work. As Paul said, itís probably something to do with the VTable.

There is a note every time you create a custom object added from the arxwizard.... for example:

Quote
#pragma once

#ifdef EZONEDBX_MODULE
#define DLLIMPEXP __declspec(dllexport)
#else
//----- Note: we don't use __declspec(dllimport) here, because of the
//----- "local vtable" problem with msvc. If you use __declspec(dllimport),
//----- then, when a client dll does a new on the class, the object's
//----- vtable pointer points to a vtable allocated in that client
//----- dll. If the client dll then passes the object to another dll,
//----- and the client dll is then unloaded, the vtable becomes invalid
//----- and any virtual calls on the object will access invalid memory.
//-----
//----- By not using __declspec(dllimport), we guarantee that the
//----- vtable is allocated in the server dll during the ctor and the
//----- client dll does not overwrite the vtable pointer after calling
//----- the ctor. And, since we expect the server dll to remain in
//----- memory indefinitely, there is no problem with vtables unexpectedly
//----- going away.
#define DLLIMPEXP
#endif



Thatís it, Thanks  Luis

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6935
  • AKA Daniel
Re: Deriving from AcDbPolyLine
« Reply #13 on: March 25, 2009, 01:11:15 AM »
It looks like Luis has found a good clue!


After additional testing, it seems everything works just dandy as long as the Arx stays loaded in the session where the object is created.
Saving, Closing, etc. all work. As Paul said, itís probably something to do with the VTable.

hmm, it must be calling the derived class to file out the polyline, does it save your member data as well?
If so that's pretty spooky :)

I wouldnít think it would save any member data, I only wanted to add my own constructor.  :mrgreen:

pkohut

  • Guest
Re: Deriving from AcDbPolyLine
« Reply #14 on: March 25, 2009, 01:23:35 AM »
hmm, it must be calling the derived class to file out the polyline,
If your saying its calling AcDbPolyline filers, that would be correct and is
be the expected behavior.

does it save your member data as well?
No can do, because the custom filer is not written.

Paul