C#.NET and AutoCAD Dictionary objects.

C#.NET and AutoCAD Dictionary objects.

Anonymous
Not applicable
4,919 Views
10 Replies
Message 1 of 11

C#.NET and AutoCAD Dictionary objects.

Anonymous
Not applicable
I have done some extensive work with Dictionaries (xRecords), and XData. I
have been able to replicate read/write of xData, and write of xrecords in
C#.NET. However, I have been able to read xrecords, or even open a
dictionary at all using .NET.

The problem is is the casting of types. Here is some code to add a
dictionary (works)...

source.AcadDocument.Dictionaries.Add(mstrDictName);

no problem, but here is what I am doing to get a reference to the dictionary
object...
=====================================================================
public static AutoCAD.AcadDictionaryClass Dictionary(ACAD.Document source)
{
//An Acad.Document object is a special object of mine which contains a
reference to an
// AutoCAD "AcadDocumentClass" object.
AutoCAD.AcadObject aoDict;
AutoCAD.AcadDictionaryClass adDict;
try
{
aoDict = source.AcadDocument.Dictionaries.Item(mstrDictName);
if (aoDict != null)
{
//adDict = (AutoCAD.AcadDictionaryClass)aoDict; //(doesn't work)
//adDict = aoDict as AutoCAD.AcadDictionaryClass; //(doesn't work)
object oD = source.AcadDocument.HandleToObject(aoDict.Handle);
//HandleToObject returns an "AcadObject" type.
adDict = oD as AutoCAD.AcadDictionaryClass; //doesn't work
return adDict;
}
else
{// does not exist in this document.
return null;
}
}
catch (Exception ex)
{
throw new InvalidOperationException("Problem reading Boundary Dictionary
from AutoCAD.", ex);
}
}
=====================================================================

As you can see i tried 3 ways. The problem is that the original object
returned from the indexer of the dictionaries collection is an "AcadObject".

First of all, it should return an AcadDictionary. How could the
Dictionaries collection contain anything but a Dictionary? It probably is
just another problem with the auto-generated wrapper. Although I am
currently using the wrapper from AcadX (www.acadx.com) which fixes the event
bug in the auto-generated wrapper.

It seems none of the 3 ways of casting to a dictionary from an AcadObject
works. Anyone have any other ideas? I have trouble casting from any
AutoCAD type to any other AutoCAD type actually... IE. AcadEntity to
AcadArc or AcadLine...

Michael
0 Likes
4,920 Views
10 Replies
Replies (10)
Message 2 of 11

Anonymous
Not applicable
Michael Lang had this to say
:
> It seems none of the 3 ways of casting to a dictionary from an
> AcadObject works.

It seems that in each case, your are attempting to cast to
AcadDictionaryClass. Have you tried casting to AcadDictionary?

While it may seem trivial, the former is a class while the latter is an
interface. Many calls return an interface supported by the object in
question and C# prohibits casting between interfaces and classes.

--
There are 10 kinds of people:
Those who understand binary and those who don't
http://www.acadx.com
0 Likes
Message 3 of 11

Anonymous
Not applicable
I just tried doing that. The cast does work. However, All the properties
don't exist on that returned object. For instance I can't query the Count
property to get the number of xrecords, or use the Item property.

===========================================
for (int i = 0; i < adDict.Count; i++)
{
AutoCAD.AcadXRecordClass xrec;
try
{
AutoCAD.AcadObject aoRec = adDict.Item(i);
xrec = aoRec as AutoCAD.AcadXRecordClass;
Boundary nextBdy = new Boundary(myDoc, myDefs, xrec);
this.Add(nextBdy);
}
catch
{}
}
===========================================

both the call to adDict.Count, and adDict.Item(i) throw the same error:

============================================================================
===========
System.NullReferenceException: Object reference not set to an instance of an
object.
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at
System.Windows.Forms.ComponentManager.
System.Windows.Forms.UnsafeNativeMethods+IMsoComonentManager.FPush
MessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.ThreadContext.RunMessageLoop(Int32 reason,
ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at Hagerman.SpacePlanner.Startup.Main() in ...
here>...
============================================================================
===========

"Frank Oquendo" wrote in message
news:A930F9A4734FEFD334A0895497EE3027@in.WebX.maYIadrTaRb...
> Michael Lang had this to say
> :
> > It seems none of the 3 ways of casting to a dictionary from an
> > AcadObject works.
>
> It seems that in each case, your are attempting to cast to
> AcadDictionaryClass. Have you tried casting to AcadDictionary?
>
> While it may seem trivial, the former is a class while the latter is an
> interface. Many calls return an interface supported by the object in
> question and C# prohibits casting between interfaces and classes.
>
> --
> There are 10 kinds of people:
> Those who understand binary and those who don't
> http://www.acadx.com
>
>
>
>
0 Likes
Message 4 of 11

Anonymous
Not applicable
Michael Lang had this to say
:
> I just tried doing that. The cast does work.

I'll have a closer look at that later tonight. I'll be sure to let you
know what I find. In the meantime: good luck.

--
There are 10 kinds of people:
Those who understand binary and those who don't
http://www.acadx.com
0 Likes
Message 5 of 11

Anonymous
Not applicable
hi michael,

i'm not familiar with xrecords and also not with c#, but i got troubles when
i used "Integer" instead of "Int16", .net changed the default datatypes, the
datatype "Integer" now depends on your operating-system/processor.
it's just a try for help, don't mind if not.

regards, alfred


"Michael Lang" schrieb im Newsbeitrag
news:DF2392AB7DB5062C7C8D1EE9B5F484FD@in.WebX.maYIadrTaRb...
> I just tried doing that. The cast does work. However, All the properties
> don't exist on that returned object. For instance I can't query the Count
> property to get the number of xrecords, or use the Item property.
>
> ===========================================
> for (int i = 0; i < adDict.Count; i++)
> {
> AutoCAD.AcadXRecordClass xrec;
> try
> {
> AutoCAD.AcadObject aoRec = adDict.Item(i);
> xrec = aoRec as AutoCAD.AcadXRecordClass;
> Boundary nextBdy = new Boundary(myDoc, myDefs, xrec);
> this.Add(nextBdy);
> }
> catch
> {}
> }
> ===========================================
>
> both the call to adDict.Count, and adDict.Item(i) throw the same error:
>
>
============================================================================
> ===========
> System.NullReferenceException: Object reference not set to an instance of
an
> object.
> at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
> at
> System.Windows.Forms.ComponentManager.
> System.Windows.Forms.UnsafeNativeMethods+IMsoComonentManager.FPush
> MessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
> at System.Windows.Forms.ThreadContext.RunMessageLoop(Int32 reason,
> ApplicationContext context)
> at System.Windows.Forms.Application.Run(Form mainForm)
> at Hagerman.SpacePlanner.Startup.Main() in ...
> here>...
>
============================================================================
> ===========
>
> "Frank Oquendo" wrote in message
> news:A930F9A4734FEFD334A0895497EE3027@in.WebX.maYIadrTaRb...
> > Michael Lang had this to say
> > :
> > > It seems none of the 3 ways of casting to a dictionary from an
> > > AcadObject works.
> >
> > It seems that in each case, your are attempting to cast to
> > AcadDictionaryClass. Have you tried casting to AcadDictionary?
> >
> > While it may seem trivial, the former is a class while the latter is an
> > interface. Many calls return an interface supported by the object in
> > question and C# prohibits casting between interfaces and classes.
> >
> > --
> > There are 10 kinds of people:
> > Those who understand binary and those who don't
> > http://www.acadx.com
> >
> >
> >
> >
>
>
0 Likes
Message 6 of 11

Anonymous
Not applicable
Yes, that is true. The "integer" datatype used in AutoCAD is a 16 bit
integer. Base integral datatypes translate as follows:

VB6 | .NET | C#.NET | VB.NET
------------------------------------------------
int | System.Int16 | short | short
long | System.Int32 | int | integer
--- | System.Int64 | long | long

IE. When you are working with xData on any AutoCAD element, the datatype
array must be an array of Int16, or "short".

"Alfred NESWADBA" wrote in message
news:D9BD82E282A1F4C8AE238D1C771DE03C@in.WebX.maYIadrTaRb...
> hi michael,
>
> i'm not familiar with xrecords and also not with c#, but i got troubles
when
> i used "Integer" instead of "Int16", .net changed the default datatypes,
the
> datatype "Integer" now depends on your operating-system/processor.
> it's just a try for help, don't mind if not.
>
> regards, alfred
>
>
> "Michael Lang" schrieb im Newsbeitrag
> news:DF2392AB7DB5062C7C8D1EE9B5F484FD@in.WebX.maYIadrTaRb...
> > I just tried doing that. The cast does work. However, All the
properties
> > don't exist on that returned object. For instance I can't query the
Count
> > property to get the number of xrecords, or use the Item property.
> >
> > ===========================================
> > for (int i = 0; i < adDict.Count; i++)
> > {
> > AutoCAD.AcadXRecordClass xrec;
> > try
> > {
> > AutoCAD.AcadObject aoRec = adDict.Item(i);
> > xrec = aoRec as AutoCAD.AcadXRecordClass;
> > Boundary nextBdy = new Boundary(myDoc, myDefs, xrec);
> > this.Add(nextBdy);
> > }
> > catch
> > {}
> > }
> > ===========================================
> >
> > both the call to adDict.Count, and adDict.Item(i) throw the same error:
> >
> >
>
============================================================================
> > ===========
> > System.NullReferenceException: Object reference not set to an instance
of
> an
> > object.
> > at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG&
msg)
> > at
> > System.Windows.Forms.ComponentManager.
> > System.Windows.Forms.UnsafeNativeMethods+IMsoComonentManager.FPush
> > MessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
> > at System.Windows.Forms.ThreadContext.RunMessageLoop(Int32 reason,
> > ApplicationContext context)
> > at System.Windows.Forms.Application.Run(Form mainForm)
> > at Hagerman.SpacePlanner.Startup.Main() in ...
> > here>...
> >
>
============================================================================
> > ===========
> >
> > "Frank Oquendo" wrote in message
> > news:A930F9A4734FEFD334A0895497EE3027@in.WebX.maYIadrTaRb...
> > > Michael Lang had this to say
> > > :
> > > > It seems none of the 3 ways of casting to a dictionary from an
> > > > AcadObject works.
> > >
> > > It seems that in each case, your are attempting to cast to
> > > AcadDictionaryClass. Have you tried casting to AcadDictionary?
> > >
> > > While it may seem trivial, the former is a class while the latter is
an
> > > interface. Many calls return an interface supported by the object in
> > > question and C# prohibits casting between interfaces and classes.
> > >
> > > --
> > > There are 10 kinds of people:
> > > Those who understand binary and those who don't
> > > http://www.acadx.com
> > >
> > >
> > >
> > >
> >
> >
>
>
0 Likes
Message 7 of 11

Anonymous
Not applicable
Michael,
The dictionaries collection can contain many objects. Try this little VBA
procedure and see what you get:

Public Sub dumpNOD()
Dim obj As AcadObject
For Each obj In ThisDrawing.Dictionaries
Debug.Print TypeName(obj)
Next obj
End Sub
--
Bobby C. Jones
www.AcadX.com
0 Likes
Message 8 of 11

Anonymous
Not applicable
"Michael Lang" wrote in message news:15F4AA9178528D5DC4A010AA272E36F0@in.WebX.maYIadrTaRb...
>
> As you can see i tried 3 ways. The problem is that the original object
> returned from the indexer of the dictionaries collection is an "AcadObject".
>
> First of all, it should return an AcadDictionary. How could the
> Dictionaries collection contain anything but a Dictionary?

Very easily. A Dictionary may contain objects other than
Dictionaries (XRecords for example, or other objects that
are derived from AcDbObject/IAcadObject).

Try this:

AutoCAD.AcadApplication Acad;
Acad = (AutoCAD.AcadApplication)
Marshal.GetActiveObject("AutoCAD.Application");

AutoCAD.AcadObject AnObject;
AnObject = Acad.ActiveDocument.Dictionaries.Item("TEST");

AutoCAD.IAcadDictionary ADict;

ADict = (AutoCAD.IAcadDictionary) AnObject;

There is a hitch when downcasting interfaces in .net, which
is that when AutoCAD creates an interface for an object, it
uses a CLSID provided by that object's ObjectARX class, and
the Interop seems to just use that interface rather than
trying to query the object for a different one.

In the case of built-in dictionaries like ACAD_GROUP and
ACAD_LAYOUTS, the ObjectARX classes that wrap these built-
in dictionaries do not return the CLSID for an AcadDictionary,
but rather they return the CLSIDs for the corresponding
Collection interfaces (IAcadGroups for the Groups dictionary,
for example). And, that's what COM wrapper ends up being
created for the built-in dictionary object. And, since those
same wrapper interfaces do not support AcadDictionary, the
QueryInterface that the Interop performs when you try to cast
an AcadObject to an AcadDictionary is failing.

For example, this code gets the AcadGroups collection from
the ACAD_GROUP dictionary object, and demonstrates that the
COM wrapper that is created for the built-in dictionary object
is actually the corresponding collection interface:

AutoCAD.AcadApplication Acad;
Acad = (AutoCAD.AcadApplication)
Marshal.GetActiveObject("AutoCAD.Application");

AutoCAD.AcadObject AnObject;

// Get the groups dictionary:

AnObject = Acad.ActiveDocument.Dictionaries.Item("ACAD_GROUP");

// Cast the groups dictionary to the Groups collection:

AutoCAD.IAcadGroups Groups;

Groups = (AutoCAD.IAcadGroups) AnObject;
0 Likes
Message 9 of 11

Anonymous
Not applicable
Ignore my gibberish below about downcasting interfaces.

Contrary to what I suggested, AutoCAD always creates the
corresponding collection interface when you try to get a
built-in dictionary object such as the group dictionary.

The confusion on my part comes largely from my routine
use of a method in AcadX that returns these same objects
as AcadDictionary objects instead of their associated
Collection object.

"Tony Tanzillo" wrote in message >
> There is a hitch when downcasting interfaces in .net, which
> is that when AutoCAD creates an interface for an object, it
> uses a CLSID provided by that object's ObjectARX class, and
> the Interop seems to just use that interface rather than
> trying to query the object for a different one.
>
> In the case of built-in dictionaries like ACAD_GROUP and
> ACAD_LAYOUTS, the ObjectARX classes that wrap these built-
> in dictionaries do not return the CLSID for an AcadDictionary,
> but rather they return the CLSIDs for the corresponding
> Collection interfaces (IAcadGroups for the Groups dictionary,
> for example). And, that's what COM wrapper ends up being
> created for the built-in dictionary object. And, since those
> same wrapper interfaces do not support AcadDictionary, the
> QueryInterface that the Interop performs when you try to cast
> an AcadObject to an AcadDictionary is failing.
>
> For example, this code gets the AcadGroups collection from
> the ACAD_GROUP dictionary object, and demonstrates that the
> COM wrapper that is created for the built-in dictionary object
> is actually the corresponding collection interface:
>
> AutoCAD.AcadApplication Acad;
> Acad = (AutoCAD.AcadApplication)
> Marshal.GetActiveObject("AutoCAD.Application");
>
> AutoCAD.AcadObject AnObject;
>
> // Get the groups dictionary:
>
> AnObject = Acad.ActiveDocument.Dictionaries.Item("ACAD_GROUP");
>
> // Cast the groups dictionary to the Groups collection:
>
> AutoCAD.IAcadGroups Groups;
>
> Groups = (AutoCAD.IAcadGroups) AnObject;
>
>
>
>
0 Likes
Message 10 of 11

Anonymous
Not applicable
I made yet another cast in an attempt to solve the problem. I casted to an
"IAcadDictionary" which is what the "AcadDictionary" interface inherits
from. The cast worked. In the locals debug window I saw a value for the
count property. However, in the line of code that reads the count property
a "System.ExecutionEngineException" was thrown. I don't understand how this
can happen? If the locals window has been able to successfully query and
display the value, why can't my code?

Here is that code...

AutoCAD.IAcadDictionary iDict = (AutoCAD.IAcadDictionary)adDict;
for (int i = 0; i < iDict.Count; i++)
{...}

Do you think this is another problem with the wrapper?

"Frank Oquendo" wrote in message
news:E13EC7E5E431A04A52D87FB0EE07AFC7@in.WebX.maYIadrTaRb...
> Michael Lang had this to say
> :
> > I just tried doing that. The cast does work.
>
> I'll have a closer look at that later tonight. I'll be sure to let you
> know what I find. In the meantime: good luck.
>
> --
> There are 10 kinds of people:
> Those who understand binary and those who don't
> http://www.acadx.com
>
>
>
>
0 Likes
Message 11 of 11

Anonymous
Not applicable
The problem had nothing to do with dictionaries.

for (int i = 0; i < adDict.Count; i ++)
{...
}

See the problem here? What datatype is the Count property?

When you hover your cursor over the Count property in .NET it says it is an
int. Do the same in VB6, and it says it is an int. int in VB6 and .NET are
not the same thing. An int in VB6 translates to a short in .NET. The
tooltip in .NET should show the Count property as a short, not an int.
Maybe the wrapper should be corrected?

I have written code that adds an xRecord, and then goes though the loop
above successfully.

"Michael Lang" wrote in message
news:F85270221929307EF6CB4A95F0E7EF14@in.WebX.maYIadrTaRb...
> I made yet another cast in an attempt to solve the problem. I casted to
an
> "IAcadDictionary" which is what the "AcadDictionary" interface inherits
> from. The cast worked. In the locals debug window I saw a value for the
> count property. However, in the line of code that reads the count
property
> a "System.ExecutionEngineException" was thrown. I don't understand how
this
> can happen? If the locals window has been able to successfully query and
> display the value, why can't my code?
>
> Here is that code...
>
> AutoCAD.IAcadDictionary iDict = (AutoCAD.IAcadDictionary)adDict;
> for (int i = 0; i < iDict.Count; i++)
> {...}
>
> Do you think this is another problem with the wrapper?
>
> "Frank Oquendo" wrote in message
> news:E13EC7E5E431A04A52D87FB0EE07AFC7@in.WebX.maYIadrTaRb...
> > Michael Lang had this to say
> > :
> > > I just tried doing that. The cast does work.
> >
> > I'll have a closer look at that later tonight. I'll be sure to let you
> > know what I find. In the meantime: good luck.
> >
> > --
> > There are 10 kinds of people:
> > Those who understand binary and those who don't
> > http://www.acadx.com
> >
> >
> >
> >
>
>
0 Likes