.NET

Reply
Contributor
mleslie00
Posts: 12
Registered: ‎08-22-2012
Message 1 of 6 (540 Views)
Accepted Solution

Passing Late-bound Variant arrays to AutoCAD

540 Views, 5 Replies
08-22-2012 01:42 PM

 

I have been attempting to pass a string array contained in a variant to the AutoCAD method AcadPlot.SetLayoutsToPlot.  I am using late binding without a Interop assembly because we have several versions of AutoCAD in this company and I would like to support all of them from the same code base.

 

The method wants a variant that contains a string array passed byref.  The Microsoft documentation on COM interop from VB.NET makes it sound like the compiler will marshal it correctly by default, but it always throws a COMException (Exception from HRESULT: 0x80070057 (E_INVALIDARG)).

 

The stack trace shows it is trying to anyway:

 

StackTrace:
       at Microsoft.VisualBasic.CompilerServices.LateBinding.InternalLateCall(Object o, Type objType, String name, Object[] args, String[] paramnames, Boolean[] CopyBack, Boolean IgnoreReturn)
       at Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateCall(Object Instance, Type Type, String MemberName, Object[] Arguments, String[] ArgumentNames, Type[] TypeArguments, Boolean[] CopyBack, Boolean IgnoreReturn)

 

But no matter how I code it I cannot get this variant array passed to AutoCAD.


I have tried:

 

Imports System.Runtime.InteropServices

 

' VB default marshalling
Dim layoutarr = New Object() {"Layout1"}
doc.Plot.SetLayoutsToPlot(layoutarr)

 


' using a VariantWrapper
Dim layoutarr = New Object() {"Layout1"}
Dim wrapper As New System.Runtime.InteropServices.VariantWrapper(layoutarr)
doc.Plot.SetLayoutsToPlot(wrapper)

 


'making a method and using a custom marshaling attribute

Dim layoutarr = New Object() {"Layout1"}
doc.Plot.SetLayoutsToPlot(MarshalVariantStringArray(layoutarr))

 

Public Function MarshalVariantStringArray(ByVal ObjIn As Object) As <MarshalAsAttribute(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_BSTR)> Object
    Return ObjIn
End Function

 


'using reflection to invoke the method
'(not sure why that would help, but it was worth a try)

Dim layoutarr = New Object() {"Layout1"}
Dim argarr(0 To 0) As Object
argarr(0) = layoutarr
doc.Plot.GetType().InvokeMember("SetLayoutsToPlot", Reflection.BindingFlags.InvokeMethod, Nothing, doc.Plot, argarr)

 

 

Does anyone know what to do in this situation?  As intensively as AutoCAD ActiveX uses variant arrays, I know this problem is going to come up again and again for me as I try to migrate code over to .NET.

 

Thanks.

 

 

*Expert Elite*
norman.yuan
Posts: 959
Registered: ‎04-27-2009
Message 2 of 6 (516 Views)

Re: Passing Late-bound Variant arrays to AutoCAD

08-23-2012 09:40 AM in reply to: mleslie00

I think in your all attempts you did the same thing: trying to pass an ARRAY of object, instead of required AN object that is an array of string.

 

This is how crapy VB.NET tries to to more things behind of scene for user. If you set Option Strict on, the error would have been caught at compiling time:

 

Dim something = New Object() {...}

 

is equivalent to

 

Dim layoutarr As object()

layoutarr=New Object(){...}

 

Hence the error: object array with one element is not the same as a single object

 

While in your cose you should have done this:

 

Dim layoutarr As object=new String(){....}

I think even this might work"

 

Dim layoutarr As String()=New String(){...}

 

Personally, if I have to write with VB.NET, I never omit the As part in the Dim statement, just make the declaration code unmistakenly clear. Whether Option Strict is on or not

 

If you use C#, you would not have run into this type of mistake.

 

 

Contributor
mleslie00
Posts: 12
Registered: ‎08-22-2012
Message 3 of 6 (474 Views)

Re: Passing Late-bound Variant arrays to AutoCAD

08-31-2012 12:54 PM in reply to: mleslie00

 

 

To let everyone know how this ended up and what I learned:

 

'marshals correctly when using Interop.AutoCAD.dll

Dim plt As AutoCAD.AcadPlot = Layout.Document.Plot

plt.SetLayoutsToPlot(layoutarr)

 

'does not work without Interop.AutoCAD.dll

Dim plt As Object= Layout.Document.Plot

doc.Plot.SetLayoutsToPlot(layoutarr)

 

'this works with no interop .dll

Dim plt As Object= Layout.Document.Plot

plt.SetLayoutsToPlot(MarshalVariantStringArray(layoutarr))

 

 

 

Public Function MarshalVariantStringArray(ByVal ObjIn AsObject) _

               As <Runtime.InteropServices.MarshalAsAttribute( _

                                   Runtime.InteropServices.UnmanagedType.SafeArray, _

                                   SafeArraySubType:=Runtime.InteropServices.VarEnum.VT_BSTR)> Object

 

ReturnObjIn

End Function

 

 

In summary, VB is using the type information in the Interop.AutoCAD.dll to know how to marshal the string array into a variant.  In the absence of that information, the default marshalling does not take care of it, but you can use attributes to tell the compiler what you are really trying to do.

 

Also for reasons that I do not really understand, the VariantWrapper object did not help the situation. AutoCAD still saw that as an invalid parameter.

 

Maybe this will help other lunatics like me that have an ongoing vested interest in using late binding.

 

Mike Leslie

 

Contributor
barts007
Posts: 12
Registered: ‎10-04-2007
Message 4 of 6 (255 Views)

Re: Passing Late-bound Variant arrays to AutoCAD

08-19-2013 12:51 PM in reply to: mleslie00

Hello,

Mike’s solution looked very promising. However, when I applied it for coping blocks I get “Invalid object array” error.

 

Dim objCollection(0) As Object

objCollection(0) = SourceFile.Blocks.Item(BlockEntity.EffectiveName)

SourceFile.CopyObjects(objCollection, TargetFile.Blocks)

 

 

Does anyone know of any solution for calling CopyObjects in late bind?

Please note that the above work in an early bind situation in which objCollection is defined as AcadBlock:

(Dim objCollection(0) As AcadBlock)

Contributor
mleslie00
Posts: 12
Registered: ‎08-22-2012
Message 5 of 6 (215 Views)

Re: Passing Late-bound Variant arrays to AutoCAD

08-21-2013 10:02 AM in reply to: barts007

 

I replicated your error (CopyObjects working correctly early bound, failing late bound), but I was not able to find a solution.

 

I could not get the MarshalAttibutes to translate this to what AutoCAD wanted.  I had thought that passing the object(s) as an IUnknown or an IDispatch inside a SafeArray made the most sense, but it just wasn't happening.

 

There really should be a way to do this, but I could not find it.

 

In theory, you could use the methods in the Marshal class to manually allocate a SAFEARRAY and to put a pointer to your object into it, but that is too involved and low-level for me to mess with right now.  I've spent too much time on this maddening problem as it is!

 

Another way to solve would be to use SendCommand to send lisp statements to do the CopyObjects for you.  See:

 

http://www.theswamp.org/index.php?topic=34676.msg399205#msg399205

 

I bet it would work, but I hate having to use multiple languages just to get something done!

 

 

Contributor
barts007
Posts: 12
Registered: ‎10-04-2007
Message 6 of 6 (208 Views)

Re: Passing Late-bound Variant arrays to AutoCAD

08-21-2013 10:37 AM in reply to: mleslie00

Thanks for the efforts on this issue.

I also hate having to use multiple languages when simple tasks should be doable with one.

I’ll try the LISP suggestion. But I wonder if CopyObjects would work in C#; it would be a more elegant solution to combine VB.NET with C# or to use C# only.

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
Are you familiar with the Autodesk Expert Elites? The Expert Elite program is made up of customers that help other customers by sharing knowledge and exemplifying an engaging style of collaboration. To learn more, please visit our Expert Elite website.

Need installation help?

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

Ask the Community