Problem with Array from C# to COM

M

mupe

Hi,

i have a problem with a Type Library, which is written in C++.
I am developing an application in C#.NET and have to use functions from
this COM-Type Library. When I use these functions in the "old" VB it
works but not in .NET. I think it is a problem with marshalling but I
could not find a solution yet.

First I included the Type Library in VS.NET 2003. Other functions of
the TL work so the reference to the Lib must be correct.

Here the piece of code in C#.NET:

app = new nwApplication();
int[] id = new int[2];
id[0] = 1;
id[1] = 2;

broadcast = app.get_broadcast(id);

app is an Application-Object of the Library. The array id has two
numbers. The get_broadcast method needs this array as parameter and
returns a broadcast-object.

Here is the PROBLEM. I seems that the get_broadcast method does not get
the content of the array correctly.

I tried it in VB like this:

Dim app, broadcast, id
id = Array(1,2)
Set app = CreateObject("NwLib.nwApplication")
Set broadcast = app.get_broadcast(id)

This works correctly.

When I look for the get_broadcast message in the object browser the
definition is like this:
nwApplication.get_broadcast(object)

When i compile my project, VS.NET creates a Interop.nw.dll. I
disassembled it to IL and looked for the definitions of the method. It
was like this:

......... get_broadcast(object marshal( struct) Value)....

Is it possible that the array in C# is not correctly marshalled?

Has anyone an idea where the problem could be? Thanks very much!

Peter
 
C

Clive Dixon

It sounds likely that the array is not being correctly marshalled. For the
C# code you are using, you may have to modify your interop assembly usiing
ildasm, a text editor, and ilasm to compile back. The marshalling in the IL
may have to be changed to something like

marshal(int32[])

or even

marshal(safearray int32)

However I don't know what the method definition looks like in your TLB. The
marshalling specified in the interop dll may well be correct but what you
are trying to do in your C# is incorrect. (I don't know what the VB Array
operation does.)
 
C

Clive Dixon

....I'm also slightly surprised that the IL doesn't mark the parameter with
[in] (unless you missed that out).

Can you supply the method signature in the type library and the full method
definition in IL?
 
M

mupe

Hi,

yes i will post the code tomorrow when I am back in office.

I already disassembled the assembly to IL and then I tried to recompile
it but that did not work.
So I disassembled it and recompiled it without any changes but then I
got an error message
that an entry point was not found and the dll was not created. I will
post the exact error message tomorrow.

Thanks so long.
 
G

Guest

I don’t know what is the problem but as far as you received the
[nwApplication.get_broadcast(object)]
Object that means you receive the address of the array object and you just
need to work out the indexes/values of your object?

If I missed your point ignore the message.

Vlad P
 
M

mupe

@Vlad P: No, the object I get back from the method needs the value of
the array for internal use. And because of the object´s behaviour I
think that it does not get the value of the array correctly.

@Clive Dixon: Recompilation with ilasm now works. I forgot some options
yesterday. Here I have the pieces of IL code I found in the nw.il after
disassembling it from Interop.Nw.dll. The names of methods could be
different to those I posted yesterday but nwBroadcast is the object I
get back from the method get_nwBroadcast().

.method public hidebysig newslot specialname virtual
instance class nwLib.nwBroadcast
marshal( interface)
get_nwBroadcast(object marshal( struct) Value) runtime
managed internalcall
{
.custom instance void
[mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32)
= ( 01 00 0F 00 00 00 00 00 )
.override nwLib.InwApplication::get_nwBroadcast
} // end of method nwApplicationClass::get_nwBroadcast

....

.property class nwLib.nwBroadcast
nwBroadcast(object)
{
.custom instance void
[mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32)
= ( 01 00 0F 00 00 00 00 00 )
.get instance class nwLib.nwBroadcast
nwLib.nwApplicationClass::get_nwBroadcast(object)
} // end of property nwApplicationClass::nwBroadcast

....

.method public hidebysig newslot specialname abstract virtual
instance class nwLib.nwBroadcast
marshal( interface)
get_nwBroadcast(object marshal( struct) Value) runtime
managed internalcall
{
.custom instance void
[mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32)
= ( 01 00 0F 00 00 00 00 00 )
} // end of method InwApplication::get_nwBroadcast

....

.property class nwLib.nwBroadcast
nwBroadcast(object)
{
.custom instance void
[mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32)
= ( 01 00 0F 00 00 00 00 00 )
.get instance class nwLib.nwBroadcast
nwLib.InwApplication::get_nwBroadcast(object)
} // end of property InwApplication::nwBroadcast

Thats all I found to this method.

In another method I get back an object which I cast to System.Array in
my C#-Code. I found in IL code that this is also marshalled as struct.
But that works without any problems?! Here the code:

.method public hidebysig newslot abstract virtual
instance object
marshal( struct)
ScanNet([in] int32 lngStartID,
[in] int32 lngEndID) runtime managed internalcall
{
.custom instance void
[mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32)
= ( 01 00 03 00 00 00 00 00 )
} // end of method InwNetwork::ScanNet


I also tried marshal( int32[]) like you told me yesterday but that did
not work. I got an error message: "parameter #1 can not be marshalled.
Invalid managed/unmanaged type combination (the object class has to be
combined with Interface, IUnknown, IDispatch, AsAny or Struct."

I hope you can help me with the code above. Thanks in advance!

Peter
 
M

mupe

I viewed the Type Lib in the C++ TypeLib Viewer and there it looked
likes this:

[id(0x0000000f), propget, helpstring("property nwBroadcast")]
HRESULT nwBroadcast(
VARIANT Value,
[out, retval] InwBroadcast** pVal);

Since my last entry I tried more things but nothing works....
 
C

Clive Dixon

Seems odd that the System.Array to VARIANT (IL 'struct') marshalling happens
one way but not the other.

Next up, I would suggest you try manually marshalling the array to VARIANT
before you pass it, using Marshal.GetNativeVariantForObject.

You will first need to allocate some memory for the out VARIANT using
Marshal.AllocCoTaskMem. I don't immediately see a way of allocating the
exact size for a VARIANT in C# so you'll probably have to try giving it a
"sufficiently large" amount (from memory a VARIANT is 16 bytes, but don't
quote me on that).

@Vlad P: No, the object I get back from the method needs the value of
the array for internal use. And because of the object´s behaviour I
think that it does not get the value of the array correctly.

@Clive Dixon: Recompilation with ilasm now works. I forgot some options
yesterday. Here I have the pieces of IL code I found in the nw.il after
disassembling it from Interop.Nw.dll. The names of methods could be
different to those I posted yesterday but nwBroadcast is the object I
get back from the method get_nwBroadcast().

.method public hidebysig newslot specialname virtual
instance class nwLib.nwBroadcast
marshal( interface)
get_nwBroadcast(object marshal( struct) Value) runtime
managed internalcall
{
.custom instance void
[mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32)
= ( 01 00 0F 00 00 00 00 00 )
.override nwLib.InwApplication::get_nwBroadcast
} // end of method nwApplicationClass::get_nwBroadcast

....

.property class nwLib.nwBroadcast
nwBroadcast(object)
{
.custom instance void
[mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32)
= ( 01 00 0F 00 00 00 00 00 )
.get instance class nwLib.nwBroadcast
nwLib.nwApplicationClass::get_nwBroadcast(object)
} // end of property nwApplicationClass::nwBroadcast

....

.method public hidebysig newslot specialname abstract virtual
instance class nwLib.nwBroadcast
marshal( interface)
get_nwBroadcast(object marshal( struct) Value) runtime
managed internalcall
{
.custom instance void
[mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32)
= ( 01 00 0F 00 00 00 00 00 )
} // end of method InwApplication::get_nwBroadcast

....

.property class nwLib.nwBroadcast
nwBroadcast(object)
{
.custom instance void
[mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32)
= ( 01 00 0F 00 00 00 00 00 )
.get instance class nwLib.nwBroadcast
nwLib.InwApplication::get_nwBroadcast(object)
} // end of property InwApplication::nwBroadcast

Thats all I found to this method.

In another method I get back an object which I cast to System.Array in
my C#-Code. I found in IL code that this is also marshalled as struct.
But that works without any problems?! Here the code:

.method public hidebysig newslot abstract virtual
instance object
marshal( struct)
ScanNet([in] int32 lngStartID,
[in] int32 lngEndID) runtime managed internalcall
{
.custom instance void
[mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32)
= ( 01 00 03 00 00 00 00 00 )
} // end of method InwNetwork::ScanNet


I also tried marshal( int32[]) like you told me yesterday but that did
not work. I got an error message: "parameter #1 can not be marshalled.
Invalid managed/unmanaged type combination (the object class has to be
combined with Interface, IUnknown, IDispatch, AsAny or Struct."

I hope you can help me with the code above. Thanks in advance!

Peter
 
M

mupe

This is the code I tried:

IntPtr mem;

int[] appId = new int[2];
appId[0] = 1;
appId[1] = 2;

mem = Marshal.AllocCoTaskMem(64); // also tried 32.....

Marshal.GetNativeVariantForObject(appId, mem);

nwBroad = nwApp.get_nwBroadcast(mem);

It does not work. It has the same effect like using appId directly as
parameter......

Do I have to edit the IL code for this too??

Thanks.
 
M

mupe

Today I tried something new. I wrote a VB6-DLL as "debug layer" which
gets the array as parameter. This dll hands the array over to the
getBroadcast method (in VB6 the whole thing works, so I chose VB6). I
returned the array from the dll back to my c# application and I got the
array with the correct numbers in it... but...
....if I hand over the array without doing anything other it does not
work.
If I define a new array in the dll and copy the content of the array I
get as parameter into that new array, it works! So it has something to
do with marshaling. The numbers in the parameter array are correct. If
I view them in debug mode they are of type Variant/Integer.
The new array i define is also Variant/Integer so I do not see any
difference?!?
 

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