AutoCAD Map 3D Developer

Reply
Active Member
ckrath
Posts: 8
Registered: ‎02-10-2011
Message 1 of 14 (2,371 Views)
Accepted Solution

Problem accessing ObjectData...

2371 Views, 13 Replies
02-10-2011 05:43 AM

I am developing a program to read Object Data in AutoCAD Map 3D 2009 using C#.Net. I use managedmapapi.dll for this.

 

My program will first read the object data, then verify it with some other data sources and then it will write the modified values  to the AutoCAD entity. To read the object data I am using OpenMode.OpenForRead.

 

Here my problem is..

 

Once I access the records in ODTables with read mode for reading Object Data, I am unable to access the records with write mode for writing. Program crashes on this line.

 

Am I missing some Object Releaser !!!

 

Please Help...

 

Thanks.

Chandan.

*Expert Elite*
Posts: 887
Registered: ‎04-27-2009
Message 2 of 14 (2,362 Views)

Re: Problem accessing ObjectData...

02-10-2011 07:30 AM in reply to: ckrath

Why don't you just get the Records in OpenForWrite mode?

 

Or if the process is like: opening OD Reocrds (for read), then your code/user need to do something, then yourcode need to update the OD Records when needed. In this case, you'd better not hold Records object you created when opening for read. You should open the Records for read, read all data into a custom objects with entity's ObjectId as key/identifier; do something (such as comparingvalues from other data source); then if update is necessary, open the Records again for write and update it. psuedo code like this:

 

foreach (ObjectId entId in targetEntityIds)

{

    //The Records only lives for read

    //Do not hold it after reading OD data for later use

    using (Records rds=ODTable.GetObjectRecords(0,OpenMode.OpenForRead,false))

   {

        ''retrieve OD data into a custom class object

    }

}

 

''Do something with the retrieved data, such as comparing with external data

 

foreach (ObjectId entId in targetEntityIds)

{

    if (the entity'd OD Data need to be updated)

   {

       //Open the Records for write

       using (Records rds=GetObjectRecords(0,OpenMode.OpenForWrite,false))

       {

          ''Update OD Data for the entity

       }

    }

}

Active Member
ckrath
Posts: 8
Registered: ‎02-10-2011
Message 3 of 14 (2,343 Views)

Re: Problem accessing ObjectData...

02-11-2011 08:55 AM in reply to: norman.yuan

 

norman.yuan,
Please have a look at my code:
First I have to show the object data in a datagridview, for that I have used a data table. to populate the columns of the data table, I have used the following code which I wrote in a different class:
public static ArrayList columnNames(string tableName)
{
	ArrayList result = new ArrayList();
	Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
	Database db = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Database;

	try
	{
		Tables tables = Autodesk.Gis.Map.HostMapApplicationServices.Application.ActiveProject.ODTables;
		Autodesk.Gis.Map.ObjectData.Table myTable = tables[tableName];
		FieldDefinitions tableDef = myTable.FieldDefinitions;
	        for(int i = 0; i < tableDef.Count; i++)
	         {
	           	FieldDefinition column = null;
	           	column = tableDef[i];
	           	result.Add(column.Name);
	         }
	}
	catch(Autodesk.Gis.Map.MapException Exp)
	{
		ed.WriteMessage(Exp.Message);
	}
	return result;
}

 

To read Object Data (written in another class):
public static string ReadODRecord(string tableName, string fieldName, ObjectId id)
{
    Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
    string fieldValue=null;
	Tables tables = HostMapApplicationServices.Application.ActiveProject.ODTables;
   	using (Records records = tables.GetObjectRecords(0, id, Autodesk.Gis.Map.Constants.OpenMode.OpenForWrite, false)) //program crashed here.
    {
        if (records.Count == 0)
        {
            ed.WriteMessage("\n There is no ObjectData record attached.");
            records.Dispose();
            return null;
        }
        foreach (Record record in records)
        {
            Autodesk.Gis.Map.ObjectData.Table table = tables[record.TableName];
            if (table.Name.CompareTo(tableName) == 0)
            {
                for (int i = 0; i < record.Count; i++)
                {
                    FieldDefinitions tableDef = table.FieldDefinitions;
                    FieldDefinition column = null;
                    column = tableDef[i];
                    string colName = column.Name;
                    if (colName.CompareTo(fieldName) == 0)
                    {
                        MapValue val = record[i];
                        fieldValue = val.StrValue;  
                        break;
                    }
                }
            }
        }
        return fieldValue; 
    }
}

 To add or update object data (written in another class):

 

 

public static void AddOrUpdOD(string TableName, ObjectId objectID, string columnName, string value)
{
	Autodesk.Gis.Map.ObjectData.Tables tables = Autodesk.Gis.Map.HostMapApplicationServices.Application.ActiveProject.ODTables;
	Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
	using(Records rcs = tables.GetObjectRecords(0, objectID, Autodesk.Gis.Map.Constants.OpenMode.OpenForWrite, false))  //program crashed here.
	{
		if(rcs.Count == 0)
		{
			Autodesk.Gis.Map.ObjectData.Table table = tables[TableName];
			Record Rec = Record.Create();
			table.InitRecord(Rec);
			table.AddRecord(Rec,objectID);
		}
	}
	
	using(Records records = tables.GetObjectRecords(0, objectID,
Autodesk.Gis.Map.Constants.OpenMode.OpenForWrite, false))
	{
		if(records.Count == 0)
		{
			ed.WriteMessage("\nData attachment failed.");
			return;
		}
		
		foreach(Record Recd in records)
		{
			Autodesk.Gis.Map.ObjectData.Table tbl = tables[Recd.TableName];
			if(tbl.Name.CompareTo(TableName) == 0)
			{
				for(int i = 0; i < Recd.Count; i++)
				{
					FieldDefinitions tblDefs = tbl.FieldDefinitions;
					FieldDefinition column = null;
					column = tblDefs[i];
					if(column.Name.CompareTo(columnName) == 0)
					{
						Recd[i].Assign(value);
						records.UpdateRecord(Recd);
						break;
					}
				}
			}
		}
	}
}

 

 

Then, to populate the datatable I have used the following code:

 

 

void BtnViewClick(object sender, EventArgs e)
{
	Autodesk.AutoCAD.ApplicationServices.Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
	Autodesk.AutoCAD.DatabaseServices.Database db = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Database;
	Autodesk.AutoCAD.EditorInput.Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
	
	try
	{
		Autodesk.AutoCAD.DatabaseServices.TypedValue[] val = {new Autodesk.AutoCAD.DatabaseServices.TypedValue((int)Autodesk.AutoCAD.DatabaseServices.DxfCode.Start, "LWPOLYLINE"), new Autodesk.AutoCAD.DatabaseServices.TypedValue((int)Autodesk.AutoCAD.DatabaseServices.DxfCode.LayerName, cmbLayer.Text)};
		Autodesk.AutoCAD.EditorInput.SelectionFilter sf = new Autodesk.AutoCAD.EditorInput.SelectionFilter(val);
		Autodesk.AutoCAD.EditorInput.PromptSelectionResult res = ed.SelectAll(sf);
		
		if(res.Status != Autodesk.AutoCAD.EditorInput.PromptStatus.OK) return;
		ArrayList featIds = new ArrayList();
		Autodesk.AutoCAD.EditorInput.SelectionSet selSet = res.Value;
		Autodesk.AutoCAD.DatabaseServices.ObjectId[] IdArray = selSet.GetObjectIds();
		
		System.Data.DataTable dt = new System.Data.DataTable(cmbLayer.Text);
		ArrayList columnNames;
		using(Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager)
		{
			using(Autodesk.AutoCAD.DatabaseServices.Transaction txn = tm.StartTransaction())
			{
				columnNames = ODUtil.ObjectData.columnNames(cmbODTable.Text);
			}
		}
		if(columnNames.Count > 0)
		{
			dt.Columns.Add("ObjectID");
			for(int i = 0; i < columnNames.Count; i++)
			{
				dt.Columns.Add(columnNames[i].ToString());
			}
		}
		else
		{
			MessageBox.Show("Object Data Table is not accessible.");
			return;
		}
		foreach(Autodesk.AutoCAD.DatabaseServices.ObjectId oid in IdArray)
		{
			Autodesk.AutoCAD.DatabaseServices.Entity ent;
			using(Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager)
			{
				using(Autodesk.AutoCAD.DatabaseServices.Transaction txn = tm.StartTransaction())
				{
					ent = (Autodesk.AutoCAD.DatabaseServices.Entity)txn.GetObject(oid, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForRead, false);
				}
			}
			Autodesk.AutoCAD.DatabaseServices.Polyline pl = ent as Autodesk.AutoCAD.DatabaseServices.Polyline;
			if(pl.Closed)
			{
				System.Data.DataRow dr = dt.NewRow();
				dr[0] = pl.ObjectId.ToString();
				for(int i = 1; i < columnNames.Count + 1; i++)
				{
					using(Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager)
					{
						using(Autodesk.AutoCAD.DatabaseServices.Transaction txn = tm.StartTransaction())
						{
							dr[i] = Convert.ToString(ODUtil.ObjectData.ReadODRecord("Parcels",columnNames[i-1].ToString(), pl.ObjectId));
						}
					}
				}
				dt.Rows.Add(dr);
			}
		}
		dt.DefaultView.Sort = dt.Columns[1].ColumnName + " ASC";
		dataGridView1.DataSource = dt.DefaultView;
	}
	catch(Autodesk.AutoCAD.Runtime.Exception Exp)
	{
		MessageBox.Show("ERROR:" + Environment.NewLine + Exp.Message + Exp.StackTrace);
	}
}

 

Program always crashes when ever i tried to open the OD records in write mode. In read mode its ok.

Is there something wrong with this code.

 

 

Thank you...

Chandan.

 

*Expert Elite*
Posts: 887
Registered: ‎04-27-2009
Message 4 of 14 (2,338 Views)

Re: Problem accessing ObjectData...

02-11-2011 09:32 AM in reply to: ckrath

I cannot spot something obviously wrong in the code. What exception you get? Here is the quote from Map ObjectARX ,NET Reference Document on the possible exception of calling Tables.GetObjectRecord() methods:

 

<QUOTE>

Remarks

Allocates an Records used to navigate through this object data table's records associated with the drawing specified by dwgId, and associates the Records with an object and initializes its open mode. Locks the specified source drawing for reading, if it is not already locked. Use Tables::GetObjectRecords to create an Records for traversing all the object data tables associated with the project. An exception of MapException will be thrown if the process failed, the error codes of exception are as follows: Map::Constants::ErrorCode::ErrorObjNotInitialized if the drawing is not active Map::Constants::ErrorCode::ErrorPermissionDenied if an error occurred when attempting to lock the specified drawing. Map::Constants::ErrorCode::ErrorWrongArgument if the object does not belong to the drawing that is associated with the iterator. Map::Constants::ErrorCode::ErrorPermissionDenied if openMode does not correspond to the mode of the AutoCAD object.

</QUOTE>

 

This description may help you to identify the cause of the crash.

 

Also, in my code, I always used Table.GetObjectRecords(), instead of Tables.GetObjectRecords(), and have not run into the crash you have had. So, why don't you try this, in case it may work for you:

 

Table tbl=.HostMapApplicationServices....ODTables[tblName];

using (Records reds=tbl.GetObjectRecords(...OpenMode.OpenForWrite))

{

 

}

Active Member
ckrath
Posts: 8
Registered: ‎02-10-2011
Message 5 of 14 (2,336 Views)

Re: Problem accessing ObjectData...

02-11-2011 11:34 AM in reply to: norman.yuan

Now I have changed the code as u have mentioned.

 

 

Tables tables = HostMapApplicationServices.Application.ActiveProject.ODTables;
Table table = tables[tableName];

using (Records records = table.GetObjectTableRecords(0, id, Autodesk.Gis.Map.Constants.OpenMode.OpenForWrite, false))
{
//codes....
}

 

 

still I am getting the same error. I have attached the jpg of exception.

 

 

Thanks.

Chandan.

*Expert Elite*
Posts: 887
Registered: ‎04-27-2009
Message 6 of 14 (2,331 Views)

Re: Problem accessing ObjectData...

02-11-2011 12:42 PM in reply to: ckrath

I just wrote a quick test from scratch (all my previous Object Data dealing code was written many years ago, against Acad Map 2006, so I decide not re-use it for this test).

 

It works OK with my AcadMap2011. Note, there is 2 command methods: "ReadOD" and "UpdateOD". In the ReadOD, the records should have been retirieved with OpenMode.OpenForRead, but I tried both OpenForread and OpenForWrite and both worked OK, so I left it as OpenForWrite.

 

I also user Table.GetObjectRecords(), just to prove that it works for me.

 

The map utility class:

 

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.Geometry;
using Autodesk.Gis.Map;
using Autodesk.Gis.Map.Project;
using Autodesk.Gis.Map.ObjectData;
using Autodesk.Gis.Map.Utilities;

namespace MapODDataUpdate
{
    public class MapTool
    {
        private StringBuilder mError = new StringBuilder();
        private ProjectModel mMapProj = null;

        public MapTool(Document dwg)
        {
            mMapProj = HostMapApplicationServices.
                Application.Projects.GetProject(dwg);
        }

        public string ErrorMsg
        {
            get { return mError.ToString(); }
        }

        public string[] GetODTableNames()
        {
            List<string> lst = new List<string>();
            StringCollection ts = mMapProj.ODTables.GetTableNames();
            foreach (string t in ts)
            {
                lst.Add(t);
            }

            return lst.ToArray();
        }

        public bool ODTableExists(string tableName)
        {
            return mMapProj.ODTables.IsTableDefined(tableName);
        }

        public bool RetrieveODRecordFromEntity(
            string tableName, ObjectId entId, out Dictionary<string, object> data)
        {
            data = new Dictionary<string, object>();

            mError.Length = 0;

            if (!mMapProj.ODTables.IsTableDefined(tableName))
            {
                mError.Append("Object Data Table is not defined: " + tableName + ".");
                return false;
            }

            Autodesk.Gis.Map.ObjectData.Table tbl = mMapProj.ODTables[tableName];

            using (Records records = mMapProj.ODTables.GetObjectRecords(
                0, entId, Autodesk.Gis.Map.Constants.OpenMode.OpenForWrite, false))
            {
                if (records.Count == 0) return true;

                foreach (Record record in records)
                {
                    for (int i = 0; i < record.Count; i++)
                    {
                        //get field name
                        FieldDefinition field = tbl.FieldDefinitions[i];
                        string fieldName = field.Name;

                        MapValue val = record[i];
                        object fieldValue = null;

                        switch (val.Type)
                        {
                            case Autodesk.Gis.Map.Constants.DataType.Integer:
                                fieldValue = Convert.ToInt32(val.Int32Value);
                                break;
                            case Autodesk.Gis.Map.Constants.DataType.Character:
                                fieldValue = Convert.ToString(val.StrValue);
                                break;
                            case Autodesk.Gis.Map.Constants.DataType.Real:
                                fieldValue = Convert.ToDouble(val.DoubleValue);
                                break;
                            case Autodesk.Gis.Map.Constants.DataType.Point:
                                Point3d pt = val.Point;
                                fieldValue = string.Format("Point({0},{1},{2})", pt.X, pt.Y, pt.Z);
                                break;
                            default:
                                fieldValue = null;
                                break;
                        }

                        if (data.ContainsKey(fieldName))
                        {
                            if (data[fieldName] == null) data[fieldName] = fieldValue;
                        }
                        else
                        {
                            data.Add(fieldName, fieldValue);
                        }
                    }
                }
            }

            return true;
        }

        public bool UpdateODRecordToEntity(
            string tableName, ObjectId entId, Dictionary<string, object> data)
        {
            mError.Length = 0;

            if (!mMapProj.ODTables.IsTableDefined(tableName))
            {
                mError.Append("Object Data Table is not defined: " + tableName + ".");
                return false;
            }

            Autodesk.Gis.Map.ObjectData.Table tbl = mMapProj.ODTables[tableName];

            using (Records records = mMapProj.ODTables.GetObjectRecords(
                0, entId, Autodesk.Gis.Map.Constants.OpenMode.OpenForWrite, false))
            {
                if (records.Count == 0) return true;

                foreach (Record record in records)
                {
                    for (int i = 0; i < record.Count; i++)
                    {
                        //get field name
                        FieldDefinition field = tbl.FieldDefinitions[i];
                        string fieldName = field.Name;

                        if (data.ContainsKey(fieldName))
                        {
                            MapValue val = record[i];

                            switch (val.Type)
                            {
                                case Autodesk.Gis.Map.Constants.DataType.Integer:
                                    val.Assign(Convert.ToInt32(data[fieldName]));
                                    break;
                                case Autodesk.Gis.Map.Constants.DataType.Character:
                                    val.Assign(Convert.ToString(data[fieldName]));
                                    break;
                                case Autodesk.Gis.Map.Constants.DataType.Real:
                                    val.Assign(Convert.ToDouble(data[fieldName]));
                                    break;
                                default:
                                    //Do nothing
                                    //If the value is Point, do something...
                                    break;
                            }
                        }
                    }

                    records.UpdateRecord(record);
                }
            }

            return true;
        }
    }
}

 The command class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;

[assembly: CommandClass(typeof(MapODDataUpdate.MyCommands))]

namespace MapODDataUpdate
{
    public class MyCommands
    {
        [CommandMethod("ReadOD")]
        public static void ReadODData()
        {
            Document dwg = Application.DocumentManager.MdiActiveDocument;
            Editor ed = dwg.Editor;

            PromptEntityOptions opt=new PromptEntityOptions("\nPick an entity: ");
            PromptEntityResult res=ed.GetEntity(opt);

            if (res.Status!=PromptStatus.OK) 
            {
                ed.WriteMessage("\n*Cancel*");
                return;
            }

            Dictionary<string, object> dic;

            MapTool map = new MapTool(dwg);
            if (!map.RetrieveODRecordFromEntity("Table1", res.ObjectId, out dic))
            {
                ed.WriteMessage("\nError: " + map.ErrorMsg);
                return;
            }

            foreach (KeyValuePair<string, object> item in dic)
            {
                ed.WriteMessage("\nFiled: {0} -> Value: {1}", item.Key, item.Value);
            }
        }

        [CommandMethod("UpdateOD")]
        public static void UpdateODData()
        {
            Document dwg = Application.DocumentManager.MdiActiveDocument;
            Editor ed = dwg.Editor;

            PromptEntityOptions opt = new PromptEntityOptions("\nPick an entity: ");
            PromptEntityResult res = ed.GetEntity(opt);

            if (res.Status != PromptStatus.OK)
            {
                ed.WriteMessage("\n*Cancel*");
                return;
            }

            PromptIntegerOptions optInt = 
                new PromptIntegerOptions("\nEnter an integer number: ");
            PromptIntegerResult resInt = ed.GetInteger(optInt);

            if (resInt.Status != PromptStatus.OK)
            {
                ed.WriteMessage("\n*Cancel*");
                return;
            }

            //Update a field in the ODtable, called "Field1", which has Integer type value
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("Field1", resInt.Value);

            MapTool map = new MapTool(dwg);
            if (!map.UpdateODRecordToEntity("Table1", res.ObjectId, dic))
            {
                ed.WriteMessage("\nError: " + map.ErrorMsg);
                return;
            }

            foreach (KeyValuePair<string, object> item in dic)
            {
                ed.WriteMessage("\nFiled: {0} -> Value: {1}", item.Key, item.Value);
            }
        }
    }
}

 So, the "UpdateOD" command world work as long as the OD Table has an Integer field called "Field1".

 

You may want to just create a test project and copy my code into it. Then create a test drawing, add an OD Table with a field called "Field1" as Integer type (plus other fields of your choice). Then verify the code runs OK or not.

 

Active Member
ckrath
Posts: 8
Registered: ‎02-10-2011
Message 7 of 14 (2,324 Views)

Re: Problem accessing ObjectData...

02-11-2011 07:54 PM in reply to: norman.yuan

Yes norman.yuan, your code is working fine.

 

I made a new command method, which shows only the object data using editor.writemessage. Here my code is working fine with write mode.

 

Now I have a strange question..

 

Is it mandatory to access the OD table records in write mode using command methods only?

In my project I am calling the readODRecord method from a win form window, where the datagridview is present.

I think, this is the reason which causes such a crash. while calling the readODRecord method, the active window is a winform, not the autocad window.

Active Member
ckrath
Posts: 8
Registered: ‎02-10-2011
Message 8 of 14 (2,319 Views)

Re: Problem accessing ObjectData...

02-12-2011 05:51 AM in reply to: ckrath

norman.yuan,

 

Problem solved.

 

I have changed the code to call the readODRecord through CommandMethod instead of calling it through button click event.

 

Thank you very much for your quick response and problem solving ideas, without which my project would not be successful.

 

Thanks.

Chandan.

*Expert Elite*
Posts: 887
Registered: ‎04-27-2009
Message 9 of 14 (2,316 Views)

Re: Problem accessing ObjectData...

02-12-2011 08:59 PM in reply to: ckrath

How your form is shown? A modal dialog or modeless dialog, or Palette window? If it is modal dialog, do you start it with a command method with CommandFlags.Session? In all of this cases, you need lock the document if the GetObjectRecords() method is called with OpenMode.OpenForWrite and called from the form:

 

psuedo-code:

 

//Assume you click a buuton to have AutoCAD MAP

//search current drawing for OD Data

private void Button_Click(...)

{

    Document dwg=Autodesk.A....MdiDocument

    using { DocumentLock lk=dwg.LockDocument())

    {

        ObjctIdCollection ids=SelectEntities();

       foreach (ObjectId id in ids)

       { 

          using (Records rds=theTables.GetObjectRecords(0, id, OpenMode.OpenForWrite, false)

          {

            ...

          }

       }

   }

}

 

In order for your OD data utility tool to be more robust, you may want to have a Document reference in it and always use it to generate a Document lock arounf GetObjectRecords() call, regardless how the utility method is used (in command method, or in a form, modal or modeless).

 

Active Member
ckrath
Posts: 8
Registered: ‎02-10-2011
Message 10 of 14 (2,314 Views)

Re: Problem accessing ObjectData...

02-12-2011 09:37 PM in reply to: norman.yuan

My form is a modeless form. Using the document locking method, its working fine.

I think this is the perfect solution for this problem.

 

Thank you norman.yuan.

 

 

Thanks.

Chandan.

You are not logged in.

Log into access your profile, ask and answer questions, share ideas and more. Haven't signed up yet? Register

Announcements
Welcome to the new Autodesk Community!
If this is your first visit, click here to get started and make the most of the Community. Let us know what you think of the new experience in the Community Feedback Forum.

Need installation help?

Start with some of our most frequented solutions to get help installing your software.

Ask the Community