.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Serialized Custom Object to xRecord Can Not Save DWG

7 REPLIES 7
Reply
Message 1 of 8
jimmytadams
2296 Views, 7 Replies

Serialized Custom Object to xRecord Can Not Save DWG

I have spent all day searching for the answer to this question, confident that someone has the answer, but everytime I find a similar question, there is never a resolution.  Hopefully, we can find an answer this time.

 

I have polyline that graphically represents a pipe.  Obviously, the polyline can not contain data for a pipe: size, material, insulationtype, insulationthickness, schedule, etc.  So, I have created a custom object with all of the appropriate properties, methods, events, etc that I need for it to behave as a pipe.  My custom object has a reference to the actual polyline with a Handle property stored as a long(since AutoCAD objects can not be serialized).  I referenced an AU 2008 class by Jeffery Geer (A Pattern for Storing Structured Data in AutoCAD Entities CP401-2) to get me as far as I am.

 

My object is marked serializable.  I serialize it to binary and attach the binary in 127 byte chunks to the xRecord of the polyline object.  My code works AWESOME!!!  Thanks Jeffery.

 

However, as soon as I attach this binary xrecord, I get the following message when I try to save the drawing:

 

"One or more objects in this drawing cannot be saved to the specified format.  The operation was not completed and no file was created”

 

I assume that since this was an AU course that some one has tried this at some point and knows how to bypass this error, but I can not find anyone willing to admit it.  Does anyone here want to stake a claim to my praise?

7 REPLIES 7
Message 2 of 8
Hallex
in reply to: jimmytadams

When I was stacking with xrecords some time ago I

got the same message

I solved this issue by passing SendStringtoExecute "_audit N "

at the end of adding xrecords I know it's not so elegant way but

it worked on A2010 for me try it too if you want

 

~'J'~

 

_____________________________________
C6309D9E0751D165D0934D0621DFF27919
Message 3 of 8
dgorsman
in reply to: jimmytadams

You might want to further define what you mean by "custom object".  That can cover a lot of ground, from block references to managed dotNET classes to ObjectARX custom objects.

----------------------------------
If you are going to fly by the seat of your pants, expect friction burns.
"I don't know" is the beginning of knowledge, not the end.


Message 4 of 8
jimmytadams
in reply to: dgorsman

Sorry.  Yes, you are correct.  Somebody else told me the same thing a short while ago.  Without getting into too much proprietary information, allow me to expound upon my concept.

 

In AutoCAD 20 years ago, hypothetically speaking, if I wanted to draw a line that represented a plumbing pipeline, I would probably draw a polyline on a plumbing pipeline layer...done.  All other data was probably handled with notes and specs, but no real data was stored on the line itself.

 

Enter the modern data rich environment of today...

 

I have been tasked with creating a drawing that behaves like the original (a polyline on a pipeline layer), but that has knowledge of it other pipeline data (size, material, schedule, etc.)  My original thought was to create custom ObjectARX objects to do the job, but 1) I am not a C++ developer, and 2) It seems like overkill for the end goal.  However, I can not inherit from a polyline object, add additional properties/methods/events, and still expect AutoCAD to persist it to the DWG database.  So, I created a custom object class in C# that has all of my pipeline properties/methods/events that also happens to have a polyline property referencing the AutoCAD Polyline.  So, I want to persist my object properties into the polyline and likewise the DWG.

 

xData seemed like a good place to start.  I placed each property into a result buffer and it worked great.  Problem - it has to come back out in the same order it went in.  Not a problem if I only had one object to work with, but I have close to 20 different custom objects, most of them based on blocks rather than polylines.  All of their properties are different and yet I wanted to create a more simplified way of getting the information.  Plus I was concerned about the 16K limit.

 

xRecords were intriguing, but initially looked like a bigger xData storage, so I initially ignored it.

 

I knew I could serialize to an external file, but then I would run the risk of the 2 files being separated at some point.

 

Then I read an article about storing serialized binary chunks into an xRecord.  Wow!  It works great until I tried to save.  AutoCAD views those objects differently and refuses to save the DWG.

 

Now, since my original post, I have since learned that it is the properties of my object that cause the failure and not necessarily the serialized binary xRecord.  My file can be saved and I now have persistent data, as long as I only use strings, integers, longs, and doubles.  As soon as I start adding object reference properties, it all falls apart, but I am working through it.

 

And, because of what a nightmare I have had trying to make this all work, I will post some code samples when this is all over.  But, in my opinion, this is the next best thing to custom objectARX.  Throw in some drawable overrules and life is grand (I don't actually need them, but I almost wish I did)...Not!  This is hard enough.

Message 5 of 8
MrRamsden
in reply to: jimmytadams

Hi

 

All interesting stuff and I'm surprised it hasn't come up on these forums before (that I can find).

 

So what's the solution? I'm creating blocks, via a jig, that hold extra data via xRecords. I also append data to the NOD that relates to the whole drawing.

 

My guess is that somewhere through the reams of added information I've tacked onto the blocks, CAD falls over and decides it doesn't want to play. The SendStringToExecute(" audit n "); method, as Hallex suggested, works but will confuse the user when they try and repeat the last command.

 

As the database.Audit() method isn't implemented yet, I would presume that the course of action would be to fix the xRecords that CAD throws a wobbly over (although they can't be that broken, as an Audit with no errors or fixes solves the problem).

 

I've looked over my xRecords and the only data I store that isn't in your list of approved types is booleans. Would the fix be to replace the bool with a string, or even 1s and 0s?

 

Now I've said it, it sounds like an easy fix, albeit one that shouldn't be necessary.

Message 6 of 8
jimmytadams
in reply to: jimmytadams

After 2 1/2 weeks of hell, I settled on one conclusion, storing binary chunks on an Xrecord does not work.  The AUDIT just removes all of my xrecords from the file, thus defeating the purpose.  So, I decided to serialize to XML and store XML strings to the xrecord.  They still have to be chunked because there is a 1024 character string limit, but it works.  It works AWESOME!!!  It even handles all custom class object references as well.  You just can't serialize an AutoCAD object onto an AutoCAD object, which makes sense.  Here is a sample of the serialization to xrecord:

 

        public void XMLSave()         {             using (MemoryStream stream = new MemoryStream())             {                 XmlSerializer serializer = new XmlSerializer(typeof(Valve));                 serializer.Serialize(stream, this);                 stream.Position = 0;                 TextReader reader = new StreamReader(stream);                 string xmlString = reader.ReadToEnd();                 ResultBuffer data = new ResultBuffer();                 data.Add(new TypedValue((int)DxfCode.Text, this.GetType().FullName));                 while (xmlString.Length > 0)                 {                     string buffer = "";                     if (xmlString.Length >= 1024)                     {                         buffer = xmlString.Substring(0, 1024);                         xmlString = xmlString.Substring(1024);                     }                     else                     {                         buffer = xmlString;                         xmlString = "";                     }                     data.Add(new TypedValue((int)DxfCode.Text, buffer));                 }                 Database db = HostApplicationServices.WorkingDatabase;                 using (Transaction tr = db.TransactionManager.StartTransaction())                 {                     acadBlockRef = tr.GetObject(acadBlockRef.ObjectId, OpenMode.ForWrite) as BlockReference;                     if (acadBlockRef.ExtensionDictionary == ObjectId.Null)                         acadBlockRef.CreateExtensionDictionary();                     using (DBDictionary dict = tr.GetObject(acadBlockRef.ExtensionDictionary, OpenMode.ForWrite, false) as DBDictionary)                     {                         if (dict.Contains("KW_PID"))                         {                             Xrecord xrec = tr.GetObject(dict.GetAt("KW_PID"), OpenMode.ForWrite) as Xrecord;                             xrec.Data = data;                             xrec.Dispose();                         }                         else                         {                             Xrecord xrec = new Xrecord();                             xrec.Data = data;                             dict.SetAt("KW_PID", xrec);                             tr.AddNewlyCreatedDBObject(xrec, true);                             xrec.ObjectClosed += new ObjectClosedEventHandler(OnDataModified);                             xrec.Dispose();                         }                     }                     tr.Commit();                 }                 data.Dispose();             }         }

 

Sorry about the formatting and lack of comments, but this really does work.  You just reverse the process to get it back:

 

        public static Valve XMLOpen(ObjectId objId)         {             Valve retval = null;             Database db = HostApplicationServices.WorkingDatabase;             XmlSerializer serializer = new XmlSerializer(typeof(Valve));             using (Transaction tr = db.TransactionManager.StartTransaction())             {                 BlockReference acadObj = tr.GetObject(objId, OpenMode.ForRead) as BlockReference;                 if (acadObj == null || acadObj.ExtensionDictionary == ObjectId.Null)                 {                     tr.Abort();                     return retval;                 }

                using (DBDictionary dict = tr.GetObject(acadObj.ExtensionDictionary, OpenMode.ForRead, false) as DBDictionary)                 {                     if (dict.Contains("KW_PID"))                     {                         using (Xrecord xrec = tr.GetObject(dict.GetAt("KW_PID"), OpenMode.ForRead) as Xrecord)                         {                             if (xrec != null)                             {                                 using (ResultBuffer rb = xrec.Data)                                 {                                     if (rb != null)                                     {                                         using (MemoryStream stream = new MemoryStream())                                         {                                             TypedValue[] tvs = rb.AsArray();                                             if (tvs != null)                                             {                                                 if (tvs[0].TypeCode == (short)DxfCode.Text)                                                 {                                                     string xmlString = "";                                                     TextWriter writer = new StreamWriter(stream);                                                     for (int i = 1; i < tvs.Length; i++)                                                     {                                                         if (tvs[i].TypeCode == (short)DxfCode.Text)                                                         {                                                             xmlString = (string)tvs[i].Value;                                                             writer.Write(xmlString);                                                         }                                                     }                                                     writer.Flush();                                                     stream.Position = 0;                                                     retval = serializer.Deserialize(stream) as Valve;                                                 }                                             }                                         }                                     }                                 }                             }                         }                     }                 }             }             return retval;         }

Message 7 of 8
ChaosInACT
in reply to: jimmytadams

Binary serialization to xrecords WORKS (though weeks of hell was correct). I'm using right now.

 

INTERESTING NOTE: while I could serialize freely in 2014, in 2015 I had to serperate all my data classes into seperate projects (with no acad dlls, important!) then digitally sign it and add it to the GAC in the registry. Then binary serialization works again. Painful.

Message 8 of 8

What are you using? Pulling your code shows missing references for Valve, OnDataModified, and Disposemissing()

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost