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 call pInterface->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! :)



Answer this question

Optional value type pointer/reference parameter during COM interop

  • Hkrabben

    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.


  • Sheng1983

    Oh, and no unsafe code please ;)
  • Slow Learner

    I don't have the link. Google for it.
  • Mark Benningfield

    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


  • Tigerwood2006

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

    Thanks for the replies though, Mattias, go Europe!


  • Geosultan

    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.



  • tthulin

    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.
  • vbnetdiscuss

    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.


  • Andrei Faber

    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 );

  • deklund

    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.



  • Optional value type pointer/reference parameter during COM interop