Author Topic: Solved - eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD  (Read 1260 times)

0 Members and 1 Guest are viewing this topic.

Bernd

  • Newt
  • Posts: 27
I'm trying to port an  AutoCAD application to BricsCAD. Most just works fine out of the box but I'm struggling with a method that draws blocks with attributes.
In AutoCAD this snippet works without a problem:
Code - C#: [Select]
  1. using (var modelSpace = (AcadDbServices.BlockTableRecord)trans.GetObject(blockTable[AcadDbServices.BlockTableRecord.ModelSpace], AcadDbServices.OpenMode.ForWrite))
  2. using (var blockDefinition = (AcadDbServices.BlockTableRecord)trans.GetObject(blockTable[block], AcadDbServices.OpenMode.ForRead))
  3. {
  4.     if (!blockDefinition.HasAttributeDefinitions)
  5.         CreateBlockDefinitionWithAttributes(trans, block, attributes.Keys);
  6.  
  7.     using (var blockReference = new AcadDbServices.BlockReference(position, blockDefinition.ObjectId))
  8.     {
  9.         modelSpace.AppendEntity(blockReference);
  10.         trans.AddNewlyCreatedDBObject(blockReference, true);
  11.  
  12.         blockReference.ScaleFactors = new AcadGeometry.Scale3d(faktor);
  13.         blockReference.Rotation = drehung;
  14.         blockReference.Layer = layer;
  15.  
  16.         foreach (AcadDbServices.ObjectId oid in blockDefinition)
  17.         {
  18.             var attributeDefinition = trans.GetObject(oid, AcadDbServices.OpenMode.ForRead) as AcadDbServices.AttributeDefinition;
  19.             if (attributeDefinition == null || attributeDefinition.Constant)
  20.                 continue;
  21.  
  22.             using (var attributeReference = new AcadDbServices.AttributeReference())
  23.             {
  24.                 /* NEXT LINE CRASHES WITH eInvalidInput */
  25.                 attributeReference.SetAttributeFromBlock(attributeDefinition, blockReference.BlockTransform);
  26.                 attributeReference.TextString = attributes[attributeDefinition.Tag];
  27.  
  28.                 blockReference.AttributeCollection.AppendAttribute(attributeReference);
  29.                 trans.AddNewlyCreatedDBObject(attributeReference, true);
  30.             }
  31.         }
  32.     }
  33.     trans.Commit();
  34. }
  35.  
CreateBlockDefinitionWithAttributes() is defined like this:
Code - C#: [Select]
  1. private static void CreateBlockDefinitionWithAttributes(AcadDbServices.Transaction trans, string blockName, IEnumerable<String> attributes)
  2. {
  3.     AcadDbServices.Database dwg = AcadDbServices.HostApplicationServices.WorkingDatabase;
  4.  
  5.     try
  6.     {
  7.         if (String.IsNullOrEmpty(blockName))
  8.             throw new ArgumentException("Blockname darf nicht leer sein");
  9.  
  10.         using (var blockTable = (AcadDbServices.BlockTable)trans.GetObject(dwg.BlockTableId, AcadDbServices.OpenMode.ForWrite))
  11.         using (var blockDefinition = (AcadDbServices.BlockTableRecord)trans.GetObject(blockTable[blockName], AcadDbServices.OpenMode.ForWrite))
  12.         {
  13.             foreach (var attribute in attributes)
  14.             {
  15.                 using (var attributeDefinition = new AcadDbServices.AttributeDefinition())
  16.                 {
  17.                     attributeDefinition.Tag = attribute;
  18.                     attributeDefinition.Visible = false;
  19.  
  20.                     blockDefinition.AppendEntity(attributeDefinition);
  21.                     trans.AddNewlyCreatedDBObject(attributeDefinition, true);
  22.                 }
  23.             }
  24.         }
  25.     }
  26.     catch (Exception e)
  27.     {
  28.         /* do something */
  29.     }
  30. }
  31.  
In BricsCAD the code crashes in line 28 attributeReference.SetAttributeFromBlock(attributeDefinition, blockReference.BlockTransform);

Any idea what might be causing this?

Thanks, Bernd
« Last Edit: February 17, 2020, 06:42:34 AM by Bernd »

owenwengerd

  • Bull Frog
  • Posts: 440
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #1 on: February 07, 2020, 06:18:59 PM »
Does it help if you AppendAttribute() before SetAttributeFromBlock()? Or maybe try calling SetDatabaseDefaults() before SetAttributeFromBlock().

Bernd

  • Newt
  • Posts: 27
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #2 on: February 10, 2020, 02:12:33 AM »
Does it help if you AppendAttribute() before SetAttributeFromBlock()? Or maybe try calling SetDatabaseDefaults() before SetAttributeFromBlock().
No, unfortunately not. Neither putting blockReference.AttributeCollection.AppendAttribute(attributeReference); in front of attributeReference.SetAttributeFromBlock(attributeDefinition, blockReference.BlockTransform); nor calling SetDataBaseDefaults() stops the code from crashing.

MickD

  • Gator
  • Posts: 3410
  • (x-in)->[process]->(y-out)
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #3 on: February 10, 2020, 02:34:56 AM »
Code - C#: [Select]
  1. using (var attributeDefinition = new AcadDbServices.AttributeDefinition()) // does the db look after this ref??
  2.  

Just thinking out loud here but maybe the attributeDefinition pointer (reference) is being released (at the end of each loop) before it's added to the db?

or

Maybe you need to add this def to the db (commit it in an inner transaction) before modifying it?

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.”

Bernd

  • Newt
  • Posts: 27
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #4 on: February 10, 2020, 06:03:49 AM »
Code - C#: [Select]
  1. using (var attributeDefinition = new AcadDbServices.AttributeDefinition()) // does the db look after this ref??
  2.  

Just thinking out loud here but maybe the attributeDefinition pointer (reference) is being released (at the end of each loop) before it's added to the db?

or

Maybe you need to add this def to the db (commit it in an inner transaction) before modifying it?

But shouldn't it have been added to the db via trans.AddNewlyCreatedDbObject()?
Nevertheless tried this:
Code - C#: [Select]
  1.     if (!blockDefinition.HasAttributeDefinitions)
  2.     {
  3.         var innerTransaction = dwg.TransactionManager.StartTransaction();
  4.         CreateBlockDefinitionWithAttributes(innerTransaction, block, attributes.Keys);
  5.     }
  6.  
and then in CreateBlockDefinitionWithAttributes():
Code - C#: [Select]
  1. ...
  2.                     foreach (var attribute in attributes)
  3.                     {
  4.                         using (var attributeDefinition = new AcadDbServices.AttributeDefinition())
  5.                         {
  6.                             attributeDefinition.Tag = attribute;
  7.                             attributeDefinition.Visible = false;
  8.  
  9.                             blockDefinition.AppendEntity(attributeDefinition);
  10.                             trans.AddNewlyCreatedDBObject(attributeDefinition, true);
  11.                         }
  12.                     }
  13.  
  14.                     trans.Commit();
  15. ...
  16.  
But still the same old eInvalidInput  :-( Before the call to CreateBlockDefinitionWithAttributes() I have two ActiveTransactions and afterwards again one as expected.
I read about BricsCAD treating objects opened via a closed transaction object differently than AutoCAD **, but I don't think this should pertain here - should it?

** I meant this: Access DBObject after transaction ends:
In AutoCAD, objects belonging to a transaction remain accessible in some cases even after the transaction has ended. In BricsCAD, all transaction resident objects are closed when the transaction ends.

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 7012
  • AKA Daniel
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #5 on: February 10, 2020, 07:05:49 AM »
I think This:

Code: [Select]
using (var attributeDefinition = new AcadDbServices.AttributeDefinition())
{
    attributeDefinition.Tag = attribute;
    attributeDefinition.Visible = false;
    blockDefinition.AppendEntity(attributeDefinition);
    trans.AddNewlyCreatedDBObject(attributeDefinition, true);
}

Should be like this:

Code: [Select]
using (var attributeDefinition = new AcadDbServices.AttributeDefinition())
{
    blockDefinition.AppendEntity(attributeDefinition);
    trans.AddNewlyCreatedDBObject(attributeDefinition, true);
    attributeDefinition.Tag = attribute;
    attributeDefinition.Visible = false;
}

   

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 7012
  • AKA Daniel
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #6 on: February 10, 2020, 07:14:46 AM »
The Using should be fine, the transaction won’t dispose the object a second time.

Also, you can cache the object ids for use outside a transaction. If you just need to open for read its faster to use the openclose transaction.
or objectid.open() in a using statement

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 7012
  • AKA Daniel
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #7 on: February 10, 2020, 07:21:46 AM »
FYI, Teigha is a bit unforgiving, It’s a great habit to use set database defaults immediately after creating a DB object, or add it to the database

Bernd

  • Newt
  • Posts: 27
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #8 on: February 10, 2020, 10:30:03 AM »
I think This:

Code: [Select]
using (var attributeDefinition = new AcadDbServices.AttributeDefinition())
{
    attributeDefinition.Tag = attribute;
    attributeDefinition.Visible = false;
    blockDefinition.AppendEntity(attributeDefinition);
    trans.AddNewlyCreatedDBObject(attributeDefinition, true);
}

Should be like this:

Code: [Select]
using (var attributeDefinition = new AcadDbServices.AttributeDefinition())
{
    blockDefinition.AppendEntity(attributeDefinition);
    trans.AddNewlyCreatedDBObject(attributeDefinition, true);
    attributeDefinition.Tag = attribute;
    attributeDefinition.Visible = false;
}
Sigh - still no success.
I'm really lost now. Just to make sure it's not the block definition itself that's causing problems, I skipped appending the attributes. This resulted in drawing the blocks correctly.
I think I posted the relevant part of the method in the beginning of the post, but just to make sure I haven't done anything stupid (BricsCAD-wise, as in AutoCAD everything runs smoothly), here's the whole method:
Code - C#: [Select]
  1. public static long DrawBlockWithAttributes(string block,
  2.                                            AcadGeometry.Point3d position,
  3.                                            string layer,
  4.                                            double faktor,
  5.                                            double drehung,
  6.                                            Dictionary<String, String> attribute,
  7.                                            bool useStartOpenClose)
  8. {
  9.     AcadDbServices.Database dwg = AcadDbServices.HostApplicationServices.WorkingDatabase;
  10.     AcadDbServices.Transaction trans = null;
  11.     long handle = -1L;
  12.  
  13.     using (trans = useStartOpenClose ? dwg.TransactionManager.StartOpenCloseTransaction() : dwg.TransactionManager.StartTransaction())
  14.     {
  15.         /*
  16.          * blockTable wird nicht in einen using-Block genommen,
  17.          * weil unter BricsCAD mit dem Disposen() der Transaktion auch ein darin allokiertes Objekt
  18.          * seine Gültigkeit verliert.
  19.          * Siehe https://www.bricsys.com/bricscad/help/en_US/CurVer/DevRef/index.html?page=source%2FdotNET_overview.htm
  20.          * */
  21.         AcadDbServices.BlockTable blockTable = null;
  22.         try
  23.         {
  24.             if (String.IsNullOrEmpty(block))
  25.                 throw new ArgumentException("Blockname darf nicht leer sein");
  26.  
  27.             AcadDbServices.ObjectId blockTableId = dwg.BlockTableId;
  28.             blockTable = (AcadDbServices.BlockTable)trans.GetObject(blockTableId, AcadDbServices.OpenMode.ForRead, true);
  29.             if (!blockTable.Has(block))
  30.                 /* Inserts the block and calls Commit() on the transaction parameter */
  31.                 InsertBlock(block, dwg, trans);
  32.  
  33.             if (trans.IsDisposed)
  34.             {
  35.                 trans = useStartOpenClose ? dwg.TransactionManager.StartOpenCloseTransaction() : dwg.TransactionManager.StartTransaction();
  36. #if BRX_APP
  37.                 blockTable = (AcadDbServices.BlockTable)trans.GetObject(blockTableId, AcadDbServices.OpenMode.ForRead, true);
  38. #endif
  39.             }
  40.  
  41.             using (var modelSpace = (AcadDbServices.BlockTableRecord)trans.GetObject(blockTable[AcadDbServices.BlockTableRecord.ModelSpace], AcadDbServices.OpenMode.ForWrite))
  42.             using (var blockDefinition = (AcadDbServices.BlockTableRecord)trans.GetObject(blockTable[block], AcadDbServices.OpenMode.ForRead))
  43.             {
  44.                 if (!blockDefinition.HasAttributeDefinitions)
  45.                     CreateBlockDefinitionWithAttributes(trans, block, attribute.Keys);
  46.  
  47.                 using (var blockReference = new AcadDbServices.BlockReference(position, blockDefinition.ObjectId))
  48.                 {
  49.                     modelSpace.AppendEntity(blockReference);
  50.                     trans.AddNewlyCreatedDBObject(blockReference, true);
  51.  
  52.                     handle = blockReference.Handle.Value;
  53.  
  54.                     blockReference.ScaleFactors = new AcadGeometry.Scale3d(faktor);
  55.                     blockReference.Rotation = drehung;
  56.                     blockReference.Layer = layer;
  57.  
  58.                     foreach (AcadDbServices.ObjectId oid in blockDefinition)
  59.                     {
  60.                         var attributeDefinition = trans.GetObject(oid, AcadDbServices.OpenMode.ForRead) as AcadDbServices.AttributeDefinition;
  61.                         if (attributeDefinition == null || attributeDefinition.Constant)
  62.                             continue;
  63.  
  64.                         using (var attributeReference = new AcadDbServices.AttributeReference())
  65.                         {
  66.                             blockReference.AttributeCollection.AppendAttribute(attributeReference);
  67.                             trans.AddNewlyCreatedDBObject(attributeReference, true);
  68.  
  69.                             //attributeReference.SetDatabaseDefaults();
  70.                             attributeReference.SetAttributeFromBlock(attributeDefinition, blockReference.BlockTransform);
  71.                             attributeReference.TextString = attribute[attributeDefinition.Tag];
  72.                         }
  73.                     }
  74.                 }
  75.                 trans.Commit();
  76.  
  77.                 return handle;
  78.             }
  79.         }
  80.         catch (Exception e)
  81.         {
  82.             // ... do something
  83.         }
  84.         finally
  85.         {
  86.             blockTable.Dispose();
  87.         }
  88.     }
  89. }
  90.  
The method that inserts the block:
Code - C#: [Select]
  1. private static ObjectId InsertBlock(string block, Database dwg, Transaction trans)
  2. {
  3.     string blockPfad;
  4.     if (!_bloecke.ContainsKey(block + ".dwg"))
  5.         blockPfad = GibPfadZuBlock(block);
  6.     else
  7.         blockPfad = _bloecke[block];
  8.  
  9.     if (String.IsNullOrEmpty(blockPfad))
  10.         throw new System.Exception(String.Format("Konnte Block {0} nicht finden",
  11.                                                  block));
  12.     /* I *think* the outer transaction has to be committed for the block insertion to be successful */
  13.     trans.Commit();
  14.     trans.Dispose();
  15.  
  16.     ObjectId blockId;
  17.     using (var dwgMitBlock = new AcadDbServices.Database(false, false))
  18.     {
  19.         dwgMitBlock.ReadDwgFile(blockPfad, FileShare.Read, true, "");
  20.         blockId = dwg.Insert(block, dwgMitBlock, false);
  21.     }
  22.  
  23.     return blockId;
  24. }
  25.  

And finally the method that creates the block definition (or rather appends the attributes to the block existing definition)...

Code - C#: [Select]
  1. private static void CreateBlockDefinitionWithAttributes(AcadDbServices.Transaction trans, string blockName, IEnumerable<String> attributes)
  2. {
  3.     AcadDbServices.Database dwg = AcadDbServices.HostApplicationServices.WorkingDatabase;
  4.  
  5.     try
  6.     {
  7.         if (String.IsNullOrEmpty(blockName))
  8.             throw new ArgumentException("Blockname darf nicht leer sein");
  9.  
  10.         using (var blockTable = (AcadDbServices.BlockTable)trans.GetObject(dwg.BlockTableId, AcadDbServices.OpenMode.ForWrite))
  11.         using (var blockDefinition = (AcadDbServices.BlockTableRecord)trans.GetObject(blockTable[blockName], AcadDbServices.OpenMode.ForWrite))
  12.         {
  13.             foreach (var attribute in attributes)
  14.             {
  15.                 using (var attributsDefinition = new AcadDbServices.AttributeDefinition())
  16.                 {
  17.                     //attributsDefinition.SetDatabaseDefaults();
  18.                     blockDefinition.AppendEntity(attributsDefinition);
  19.                     trans.AddNewlyCreatedDBObject(attributsDefinition, true);
  20.  
  21.                     attributsDefinition.Tag = attribute;
  22.                     attributsDefinition.Visible = false;
  23.                 }
  24.             }
  25.         }
  26.     }
  27.     catch (Exception e)
  28.     {
  29.         // ... do something
  30.     }
  31. }
  32.  
Anyone mind testing this code and see if it works? Or seeing something obviously stupid?

MickD

  • Gator
  • Posts: 3410
  • (x-in)->[process]->(y-out)
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #9 on: February 10, 2020, 06:26:22 PM »
Got most of this setup for testing, still need a few pieces and I can take a look a bit later today if time permits.

Where are the bold italic objects defined?
if (!_bloecke.ContainsKey(block + ".dwg"))
                blockPfad = GibPfadZuBlock(block);
            else
                blockPfad = _bloecke[block];
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.”

Bernd

  • Newt
  • Posts: 27
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #10 on: February 11, 2020, 02:05:42 AM »
Got most of this setup for testing, still need a few pieces and I can take a look a bit later today if time permits.

Where are the bold italic objects defined?
if (!_bloecke.ContainsKey(block + ".dwg"))
                blockPfad = GibPfadZuBlock(block);
            else
                blockPfad = _bloecke[block];

Code - C#: [Select]
  1. private static Dictionary<string, string> _bloecke; // blockname to blockpath, e.g. "myBlock", "C:\temp\myBlock.dwg"
  2. private static string _searchPath;  // directory that contains blocks (drawings)
  3.  
  4. internal static string GibPfadZuBlock(string blockName)
  5. {
  6.     try
  7.     {
  8.         if (String.IsNullOrEmpty(_searchPath) || String.IsNullOrEmpty(blockName))
  9.             return String.Empty;
  10.  
  11.         if (Path.GetExtension(blockName) != ".dwg")
  12.             blockName = Path.ChangeExtension(blockName, ".dwg");
  13.  
  14.         string[] files = Directory.GetFiles(_searchPath, blockName, SearchOption.AllDirectories);
  15.  
  16.         if (files.Length == 0)
  17.             return String.Empty;
  18.  
  19.         _bloecke[blockName] = files[0];
  20.         return files[0];
  21.     }
  22.     catch (System.Exception e)
  23.     {
  24.         // do something...
  25.     }
  26. }
  27.  
Thanks for taking your time!

n.yuan

  • Bull Frog
  • Posts: 286
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #11 on: February 11, 2020, 09:42:19 AM »
Since you say the same code works OK in AutoCAD, but not in BricsCAD, there must be something BricsCAD implements in its .NET API subtly different. With this in mind, let me try a shoot in the dark.

the CreateBlockDefinitionWithAttributes() method, if I understand it correctly is to add AttributeDefinitions to an existing block definition. I also assume you add multiple attribute definitions mostly, especially in your test run, which resulted in the said error. This is where I thought the code is a bit weird: the code adds multiple attributes without setting any of the attribute properties, especially its position (meaning they would stack one on top of another). This would be fine theoretically (since you said it worked in AutoCAD), but practically, you would not want many attributes at the same position in most cases. The point here is that could be the attributeDefinition added in this way is not properly initialized due to how BricsCAD implement its .NET API (thus different from AutoCAD subtly)?

You might want to try these 2 ways to see what happens:

1. Only run the code of CreateBlockDefinitionWithAttributes() so an existing block definition is changed (attributes are added). Then in the drawing, you manually insert a block reference to it,see if you get error, or if the block reference is inserted correctly (with attribute references created, of course). You can also use block editor to examine the block definition to verify the added attribute definitions.

2. In the CreateBlockDefinitionWithAttributes(), after new AttributeDefinition created, explicitly set its position. Maybe, just maybe, this is something causes the trouble?

Bernd

  • Newt
  • Posts: 27
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #12 on: February 14, 2020, 07:49:39 AM »
Hi Norman,
sorry for answering so late.

the CreateBlockDefinitionWithAttributes() method, if I understand it correctly is to add AttributeDefinitions to an existing block definition. I also assume you add multiple attribute definitions mostly, especially in your test run, which resulted in the said error. This is where I thought the code is a bit weird: the code adds multiple attributes without setting any of the attribute properties, especially its position (meaning they would stack one on top of another).

Everything correct. The attributes are not supposed to be visible in the drawing but only to get information about them in the property grid. This is why I don't care about their positions.

1. Only run the code of CreateBlockDefinitionWithAttributes() so an existing block definition is changed (attributes are added). Then in the drawing, you manually insert a block reference to it,see if you get error, or if the block reference is inserted correctly (with attribute references created, of course). You can also use block editor to examine the block definition to verify the added attribute definitions.

What I did was, I skipped the part of adding the attribute definitions to to the block reference. I think this is what you suggested. Then examining the block via block attribute manager shows the added attributes. Trying to insert the block via ''INSERT'' yields "Invalid input. BricsCAD cannot finish this operation" (more or less - translated from german). So you're on the right track.

2. In the CreateBlockDefinitionWithAttributes(), after new AttributeDefinition created, explicitly set its position. Maybe, just maybe, this is something causes the trouble?

Still to be tested...

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 7012
  • AKA Daniel
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #13 on: February 14, 2020, 08:04:30 AM »
Do you have a sample VS Solution?

Bernd

  • Newt
  • Posts: 27
Re: eInvalidInput in AttributeReference.SetAttributeFromBlock() in BricsCAD
« Reply #14 on: February 14, 2020, 08:17:37 AM »
I'll try to put one together,  but it might take a bit. Right now I'm struggling with some really weird behaviour regarding MPolygons with bulges  :woow: . But that'll become another thread  :hahanot: