need help with ArraySubType in MarshalAs

J

jg

I having trouble with the keyword "ArraySubType" in Visual basic studio beta 2.

I got "Error 1 Name 'ArraySubType' is not declared. Test.vb 1061 4 ..."
for
' array TO COM LPStr

Public Function etst(ByVal iCnt As Integer, _

<MarshalAs(UnmanagedType.LPArray, _

ArraySubType = UnmanagedType.LPStr, SizeParamIndex:=0)> ByRef strMatchReslts() As String) As Integer


The Only thing I have different from the documentation is
- the use of ByVal and ByRef.. However, if I don't use nay, VB will put in ByVal anyway.
- I am declaring a function instead of sub


According to the "Passing Arrays to COM" section of the article
"Default Marshaling for Arrays" on

http://msdn.microsoft.com/library/d...uide/html/cpconDefaultMarshalingForArrays.asp

ELEMENT_TYPE_SZARRAY
When a method containing an ELEMENT_TYPE_SZARRAY parameter (one-dimensional array) is exported from a .NET assembly to a type library, the array parameter is converted to a SAFEARRAY of a given type. The same conversion rules apply to the array element types. The contents of the managed array are automatically copied from managed memory into the SAFEARRAY. For example:

Managed signature

[Visual Basic]
Sub [New](<MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> _
ar() As Long, size as Integer )
Sub [New](<MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> _
ar() As String, size as Integer )
Sub [New](<MarshalAs(UnmanagedType.LPArray, _
ArraySubType= UnmanagedType.LPStr, SizeParamIndex:=1)> _
ar() As String, size as Integer )
[C#]
.......Unmanaged signature

HRESULT New(long ar[]);
HRESULT New(BSTR ar[]);
HRESULT New(LPStr ar[]);
 
D

Dragon

It seems that you missed a colon before =, i.e. place

~ ArraySubType := UnmanagedType.LPStr ~

instead of

~ ArraySubType = UnmanagedType.LPStr ~

What you are doing now is passing the boolean value (ArraySubType =
UnmanagedType.LPStr) for a named parameter (impossible though).

Hope this helps,
Roman
 
J

jg

Thank you very much. I should found it myself last night!

so now I have a test function like this
' array TO COM LPStr
Public Function etst(ByVal iCnt As Integer, _
<MarshalAs(UnmanagedType.LPArray, ArraySubType:=UnmanagedType.LPStr,
SizeParamIndex:=0)> ByRef strMatchReslts() As String) As Integer
Dim strMyC As String() = {"abcdef string 1", "abcdef string 2",
"abcdef string 3", "abcdef string 4", "abcdef string 5"}
strMatchReslts = strMyC
Return strMyC.GetUpperBound(0)
End Function
in my .net test com class

However after compilation of this test Com class, I tried call from a com
client like ( the syntax my look a bit odd because I using it from an OO
application tool that was buildt with C from some vendor)
long lsz = 5, l
String strArr[]
for l = 1, 1, lsz
strArr[l] = fill(100)
next

/* ...... after steting up and intializing myCOMObj
.... use myCOMObj call some simple test function within COM class with non
array arguments
... and they worked
*/

// so myCOMObj has been setup correctly wihtotu question

long iActualSzReturned
iActualSzReturned = myCOMObj .

myComObj.eTst(lsz, strArr)
// ooops I got runtime error here, unfortunately, it does not tell me
the exact message
// other then Error calling external object function etst at line 15
// which is the above statment
 
D

Dragon

Uhh,.. OK, haven't dealt with COM classes made in VB .NET before, but I
believe you should make the array parameter ByVal, i.e.:

~
ByVal strMatchReslts() As String
~

Roman
 
J

jg

thanks

but how would the use of byVal help me to pass changed values back to COM
caller?
 
D

Dragon

You seem to misunderstand how arrays are represented and marshalled.
When you make a ByVal parameter, which type's an array, and specify the
LPArray behaviour, it means that this function expects to find in this
parameter the address of first element of the array. When the function
ends, it is supposed to put the changed values back to array at the
specified address.

So, that's what happens:

1. The calling sub ("A") has a C-style array, i.e. values are just
located one after another in a memory block. A wants to call a managed
COM subroutine ("B") with that array.
2. A sees that second parameter of B accepts a LPArray, so it passes
array length as a first parameter, then takes the address of first
element (Ref in the array and passes it as a second one.
3. B receives address of the first array element and its length, so it
makes a System.Array and fills it with values extracted from address
given; number of elements is obviously determined by first param.
4. -- YOUR CODE GOES HERE NO MATTER WHAT IT DOES --
5*. B takes values of System.Array and puts them back to address
specified by A.
6. A continues its work with new values.

#5 is marked with asterisk, because I'm not sure if it's completed — as
I stated before, I never have been working with such code.
Have you tried making your parameter ByVal? If yes and it doesn't work
then try to apply InAttribute & OutAttribute to param's attributes. This
should force values to be passed forth and back.

When you make array parameter ByRef, it expects the address of an
address to first element. As caller passes only address to element you
end up with garbage. Maybe, in C you'd be able to handle this, but in
simple languages you can't, so avoid it.

Hope this helps,
Roman
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top