Of course, not all is well, since AdsName was a pair of int32 until recently (R 18.1?), so that may fail with previous versions. Am I at least correct in my handling of the ResultBuffer pointer?
Yes, using 'out IntPtr' is equivalent to resbuf**, and you can use 'int/long[2]'
for the adsname argument, instead of out AdsName. If the AdsName struct
changed from int to long you would need to have release-dependent code.
I don't know what the author of the article was thinking, but there's no
need for any unsafe code for what that code is doing.
If one really wanted to do what that article shows (which I would not
advise, because what the article doesn't point out is that doing that
will overwrite the user's
Previous selection set), you only need to
P/Invoke acedSSGet(), and the rest can be done entirely in managed
code.
See:
Autodesk.AutoCAD.ApplicationServices.Marshaler.AdsNameToSelectionSet()To get the argument (a pointer to a 2-element int[] or long[] array), you would
use something like this:
long[2] array = // assigned to result of acedSSGet():
SelectionSet ss = null;
GCHandle handle = GCHandle.Alloc(array, GCHandleType.Pinned);
try
{
ss = Marshaler.AdsNameToSelectionSet( handle.AddrOfPinnedObject() );
}
finally
{
handle.Free();
}
Once you have the managed selection set, the SelectedObject class
exposed by the SelectionSet provides access to nested entities and
does all the grunt work like calling acedSSNameX() and so forth.
edit:
After checking what I wrote above, it turns out that the AdsName struct
is the best way to go, because it allows you to entirely avoid release-
dependent code.
Use an AdsName as the last argument to acedSSGet() (with the 'ref'
modifier, because it's value will be written and used in the managed
caller), and use something like this to convert the AdsName result
to a managed selection set:
AdsName adsname // assigned to result of acesSSGet():
SelectionSet ss = null;
using( PinnedObject pinned
= new PinnedObject
( adsname
) ) {
ss = Marshaler.AdsNameToSelectionSet( pinned );
}
// Wrapper for GCHandle to correctly handle Free():
public class PinnedObject : IDisposable
{
private GCHandle handle;
public PinnedObject( object obj )
{
if( obj == null )
throw new ArgumentNullException
("objectToPin"); this.handle = GCHandle.Alloc( obj, GCHandleType.Pinned );
}
public IntPtr Address
{
get
{
return handle.AddrOfPinnedObject();
}
}
public static implicit operator IntPtr( PinnedObject src )
{
return src.handle.AddrOfPinnedObject();
}
void IDisposable.Dispose()
{
if( handle.IsAllocated )
{
handle.Free();
}
}
}
The above should work regardless of the release or what
the AdsName struct is defined to be.
One caveat I will point out, which speaks directly to what is
(IMO) a major design flaw of the managed API, is that doing
this with a very large selection set will not be terribly fast,
because (the major design flaw), managed selection sets are
not wrappers around native selection sets (yes, IMO they
should be wrappers, since this issue seems to keep cropping
up and in fact, AdsNameToSelectionSet() and the AdsName
struct are the 'band-aids').