Optional value type pointer/reference parameter during COM interop

Hey,

I am facing an interesting interop problem that I haven't found a solution for yet.

Let's say a COM method takes a pointer to a simple struct:

HRESULT Foo(LPBAR pBar);

Let's also say that this parameter is optional and the callpInterface->Foo(NULL); is perfectly valid. Now, if the structure BAR is defined in C#:

[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct Bar
{
// ...
};

and the COM interop method call is also defined:

[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public void Foo(ref Bar);

In this case, there is no way to be able to call Foo(null) in C#, although it would be valid. The question is: how do I let the Marshaler know that it should pass a null value and not always expect a valid variable of type Bar? Same with Guid or other built in value types..

This is not a problem when dealing with reference types such as strings or arrays but in the case of value types?

Quick answer highly appreciated, experts! :)

[1122 byte] By [RGabo] at [2007-12-23]
# 1
Oh, and no unsafe code please ;)
RGabo at 2007-8-30 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 2

You can change the parameter type to Bar[] instead, and then either pass in a single-element array or null. Another option is to make the parameter type IntPtr but it requires more work.

MattiasSj?gren at 2007-8-30 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 3

Well, Bar[] would work, but it would be more code, more memory and wouldn't even make sense as that function does not take multiple Bars but only one. IntPtr is not only more work, but it loses type information.

Any other ideas? It could easily be that there is no language construct for this. In that case, I'll just live with it.

RGabo at 2007-8-30 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 4
RGabo wrote:

Any other ideas? It could easily be that there is no language construct for this. In that case, I'll just live with it.

Exactly, there's no language support for this. Ref parameters must refer to a location. The workarounds are the ones I suggested. That or using a pointer type instead (which only works for a subset of all struct types, plus you alredy refused that option). It's a tradeoff you have to make.

MattiasSj?gren at 2007-8-30 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 5

So where is the suggestion box? anders.hejlsberg@microsoft.com? :)

Thanks for the replies though, Mattias, go Europe!

RGabo at 2007-8-30 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 6
Actually there is no suggestion to make :). This is by design. Value types must have a value. When they are used there is no need to check for null. That's one reason they are faster then reference types.
LucianBargaoanu at 2007-8-30 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 7

I tend to argue with that. We are not passing a value type here but a pointer to a value type. Also, we are talking about interoperability and not value types vs reference types in general.

The current C# design does not allow you to express your intent to pass a NULL value to unmanaged code where the signature of the method requires to pass a value type by reference. I would even guess that the value type gets boxed and unboxed when passed as a reference, although I could see some JIT tricks here.

Anyways, I don't have enough time to dig deep into this issue and see if something can be done on the IL level, I am fine with this limitation.

RGabo at 2007-8-30 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 8
From MSDN:

Passing Null Instead of a Reference to a Value Type

The following IDL declaration shows an IDL pointer to a structure.

HRESULT TestPassingNull([in, unique] Point* refParam);

Tlbimp.exe imports the parameter as a reference to the value type Point. In C# and Visual Basic 2005, a null reference (Nothing in Visual Basic) cannot be passed as a parameter when a reference to a value type is expected. If the COM function requires a null (Nothing) parameter, you can alter the signature by editing the MSIL.

Search MSIL for

.method public hidebysig newslot virtual 

instance void TestPassingNull(

[in] valuetype MiscSrv.tagPoint& refParam)

runtime managed internalcall

Replace with

.method public hidebysig newslot virtual 

instance void TestPassingNull([in] native int) runtime managed internalcall

The altered signature enables you to pass a null value. However, when you need to pass some real values, you must use the methods of the Marshal class, as the following example shows.

C#
tagPoint p = new tagPoint();

p.x = 3;

p.y = 9;

IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( p ));

Marshal.StructureToPtr( p, buffer, false );

tst.TestPassingNull( buffer );

Marshal.FreeCoTaskMem( buffer );

tst.TestPassingNull( IntPtr.Zero );

LucianBargaoanu at 2007-8-30 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 9

Lucian, that makes sense too, I use Marshal.StructureToPtr and PtrToStructure when dealing with PROPVARIANTs. The problem here is that you are making the interop layer more complex. Of course, with a wrapper around it you can create a method TestPassingNull(Point? pont) and use Marhal.StructureToPtr internall or just pass IntPtr.Zero.

Can you give me the link to MSDN where you found this?

GaborRatky at 2007-8-30 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 10
I don't have the link. Google for it.
LucianBargaoanu at 2007-8-30 > top of Msdn Tech,.NET Development,Common Language Runtime...

.NET Development

Site Classified