Run the Test1 command, select some objects and then keep pressing enter until AutoCAD crashes or until you get bored. Then, run the Test2 command and do it again.
After trying to create 128 ResultBuffer objects, the Test2 command crashes but the Test1 command does not (or at least, for me, that is).
In AutoCAD 2013, I get a Autodesk.AutoCAD.Runtime.Exeption with ErrorStatus of InvalidInput at the SelectioSet GetAdsName() method that has been called by the ResultBuffer TypedValueToResbuf(TypedValue value) method.
Does anybody seem to know what is going on here?
Yes. You're calling GetAdsName(), which creates a native selection set that must be freed by calling acedSSFree().
Use reflector to search for acedSSFree(), and then use Analyze to see what code is calling it. Without calling acedSSFree() on a native selection set, you will hit the wall at 128.
Here's the implementation of the Editor's RunCommand() method that illustrates it.
internal unsafe PromptStatus RunCommand(params object[] parameter)
{
int length;
if (parameter != null)
{
length = parameter.Length;
}
else
{
length = 0;
}
int num4 = length;
int num = 5100;
if (length == 0)
{
resbuf resbuf;
*((short*) (&resbuf + 8)) = 5000;
num = acedCmd((resbuf modopt(IsConst)*) &resbuf);
}
int index = 0;
while (true)
{
if ((index >= num4) || (num != 5100))
{
break;
}
object o = parameter[index];
if ((o
!= null) && (o
.GetType() == typeof(ResultBuffer
))) {
num = acedCmd((resbuf modopt(IsConst)*) (o as ResultBuffer).UnmanagedObject.ToPointer());
}
else
{
resbuf* resbufPtr = acutNewRb(5000);
if (resbufPtr == null)
{
throw new OutOfMemoryException
(); }
try
{
if (o != null)
{
IntPtr rb
= new IntPtr
((void*) resbufPtr
); Marshaler.ObjectToResbuf(o, rb);
}
num = acedCmd((resbuf modopt(IsConst)*) resbufPtr);
}
finally
{
if (resbufPtr[8L] == 5007)
{
acedSSFree((long modopt(IsConst)* modopt(IsConst) modopt(IsConst)) (resbufPtr + 16L));
}
acutRelRb(resbufPtr);
}
}
index++;
}
if (num != -5002)
{
return ((num != 5100) ? PromptStatus.Error : PromptStatus.OK);
}
return PromptStatus.Cancel;
}
In the 2013 API, there is a class called
DisposableSelectionSet, which will call acedSSFree() from it's implementation of Finalize(), and you can use that to avoid having to P/Invoke acedSSFree():
public sealed class DisposableSelectionSet : IDisposable
{
// Fields
private AdsName m_adsName;
// Methods
public DisposableSelectionSet(SelectionSetDelayMarshalled selectionSet)
{
AdsName name = selectionSet.Name;
this.m_adsName = name;
}
private unsafe void ~DisposableSelectionSet()
{
$ArrayType$$$BY01_J e$$$by_j;
*((long*) &e$$$by_j) = this.m_adsName.name1;
*((long*) (&e$$$by_j + 8)) = this.m_adsName.name2;
if ((*(((long*) &e$$$by_j)) != 0) || (*(((long*) (&e$$$by_j + 8))) != 0))
{
Interop.CheckAds(acedSSFree((long modopt(IsConst)* modopt(IsConst) modopt(IsConst)) &e$$$by_j));
}
}
public sealed override void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose([MarshalAs(UnmanagedType.U1)] bool flag1)
{
if (flag1)
{
this.~DisposableSelectionSet();
}
else
{
base.Finalize();
}
}
}