none
PInvoke and nullable types RRS feed

  • Question

  •  

    Hi,

     

    I am referencing a function in an external unmanaged C++ DLL (from a C# client) as follows:

     

    Code Snippet

    [DllImport("external.dll")]

    public static extern int SomeFunction (CustomType customType);

     

     

    The function is expecting a parameter of 'CustomType'. I have created an identical type in my C# as a struct.

    According to the (limited) documentation I have for the C++ DLL, this function can also be passed 'null', which I would like to do.

     

    I have tried to make 'CustomType' a nullable type as follows:

     

    Code Snippet

    [DllImport("external.dll")]

    public static extern int SomeFunction (CustomType ? customType);

     

     

    However, if I call this code and pass 'null' into the function, I receive the following error:

     

    Code Snippet
    System.Runtime.InteropServices.MarshalDirectiveException: Cannot marshal 'parameter #1': Generic types cannot be marshaled

     

     

    Has anybody got any idea how I can pass 'null' to the external function? (I cannot modify the code in the external DLL).

     

    Am very grateful for any help.

     

    Martin

     

     

    Monday, May 12, 2008 3:42 PM

Answers

  • Oh, missed that.  Declare an overload like this:

        static extern int SomeFunction(IntPtr arg);

    And call like this:

        SomeFunction(IntPtr.Zero);
    Tuesday, May 13, 2008 8:35 AM
    Moderator
  • You are definitely passing null with the 2nd overload.  Other things to fret about are:

    - DllImportAttribute.CallingConvention, default is __stdcall which is appropriate for most functions exported from a DLL, could be Cdecl if the original DLL was built with a DEF file.
    - StructLayout.CharSet, default is Auto but old 'C' code almost always requires Ansi.  Only relevant if CustomType contains strings.
    - StructLayout.Pack, default is 8 and affects the alignment of members of CustomType.  You'd have to guess that one, 1, 4 and 8 are typically used in old 'C' code.

    Tuesday, May 13, 2008 12:55 PM
    Moderator

All replies

  • If it can be passed null then the C/C++ declaration for the function must be

      int SomeFunction(CustomType* arg);

    The equivalent P/Invoke declaration is

      static extern int SomeFunction(ref CustomType arg);

    Which makes sense, structures are rarely passed by value in C/C++. 
    Monday, May 12, 2008 6:39 PM
    Moderator
  • Hi,

     

    Thanks for your reply. If I create the P/Invoke declaration as you suggest above:

     

    Code Snippet
    static extern int SomeFunction(ref CustomType arg);

     

     

     

    Then I cannot pass 'null' to it (as I am trying to do), I receive the following error:

     

    Code Snippet
    Error 2 Argument '2': cannot convert from '<null>' to 'ref CustomType arg'

     

     


    Just to recap - the C++ function allows 'null' to be passed in place of a custom type. I want to be able to pass null from my C#.

     

    Thanks again for any help!

    Martin

    Tuesday, May 13, 2008 8:27 AM
  • Oh, missed that.  Declare an overload like this:

        static extern int SomeFunction(IntPtr arg);

    And call like this:

        SomeFunction(IntPtr.Zero);
    Tuesday, May 13, 2008 8:35 AM
    Moderator
  • Thanks for your help, Hans!

     

    All of the code above compiles and runs fine...from the C# end. However, the C++ function is being extremely unhelpful and returns an error code which means 'Invalid Parameter'. (I have no documentation to help with this error message).

     

    The C++ function is defined in the documentation as:

     

    Code Snippet

    Result API SomeFunction (UINT uDatasetID, CustomType lpCustomType, LPHANDLE phDataset);

     

     

     

    I am calling this with either of these overloaded methods from the C#.

     

    Code Snippet

    [DllImport("external.dll")]

    public static extern UInt16 SomeFunction(UInt32 uDatasetID, ref CustomType lpCustomType, ref IntPtr lphDataset);

     

    [DllImport("external.dll")]

    public static extern UInt16 SomeFunction(UInt32 uDatasetID, IntPtr lpCustomType, ref IntPtr lphDataset);

     

     

    ...and then calling these P/Invokes with either:

     

    Code Snippet

    int resultCode = SomeFunction(1, ref customType, ptr);

     

    int resultCode = SomeFunction(1, IntPtr.Zero, ptr);

     

     

    I am wondering if I can somehow pass 'null' as the second parameter, in line with this guidance from the C++ documentation:

     

    The second parameter is a pointer to a CustomType structure.

    If not required, pass a null (nil) in this parameter.

     

     This post is getting long and rambly, and is probably unclear now! Many thanks if you have taken the trouble to read this  far!

     

    Martin

    Tuesday, May 13, 2008 11:31 AM
  • You are definitely passing null with the 2nd overload.  Other things to fret about are:

    - DllImportAttribute.CallingConvention, default is __stdcall which is appropriate for most functions exported from a DLL, could be Cdecl if the original DLL was built with a DEF file.
    - StructLayout.CharSet, default is Auto but old 'C' code almost always requires Ansi.  Only relevant if CustomType contains strings.
    - StructLayout.Pack, default is 8 and affects the alignment of members of CustomType.  You'd have to guess that one, 1, 4 and 8 are typically used in old 'C' code.

    Tuesday, May 13, 2008 12:55 PM
    Moderator
  • Hi Hans,

     

    Once again - your help is much appreciated.

     

    I am going to put on hold trying to pass 'null' for the time being. I think I have successfully created a C# version of the C++ custom type, and am going to try and pass this instead. At the moment I get an 'access violation'- have posted a thread about this here (if you are interested):

     

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3340461&SiteID=1&mode=1

     

    I shall return to passing 'null' later, and will try out your hints above.

     

    Many thanks,

    Martin

    Wednesday, May 14, 2008 9:07 AM
  • Hi Hans,

     

    I did try modifying the DLLImport and StructLayout decorations in the ways you suggested, but still no luck. The C++ stubbornly returns 'Invalid Parameter' if I try to pass 'IntPtr.Zero'.

     

    I have also tried passing my own interpretation of the custom type over, and on first appearances the C++ function seems happy. But the values I am assigning inside my Struct seem very odd, so I suspect that the Struct is losing the sequence of its members during marshaling. I have posted another thread about this.

     

    Thanks again,

    Martin

     

    Thursday, May 15, 2008 8:53 AM
  • burdock,

     

    Thanks very much for your questions and replies. I notice you have marked your reply in the thread P/Invoke and 'access violation' in target DLL. In my opinion, you have done a good job there and the thread will help others with this kind of problems.

     

    Since the replies from nobugz have provided the valuable suggestions, I would like to mark his reply as Answered in order to show his value to your problem. Thanks again for your questions and understanding.

    Friday, May 16, 2008 4:13 AM
  • Nice catch Bruno!
    Friday, May 16, 2008 9:03 AM
    Moderator
  • Sorry guys, have belatedly clicked on the 'mark post useful' buttons Smile

     

    Will use these in future!

    Friday, May 16, 2008 3:11 PM