vmeln
vmeln

Reputation: 1309

Wrong value in field after marshalling

I'm trying to marshal primitive structure from C++ to C#, and have following code:

using System;
using System.Runtime.InteropServices;

namespace dotNet_part
{
    class Program
    {
        static void Main(string[] args)
        {
            Custom custom = new Custom();
            Custom childStruct = new Custom();

            IntPtr ptrToStructure = Marshal.AllocCoTaskMem(Marshal.SizeOf(childStruct));
            Marshal.StructureToPtr(childStruct, ptrToStructure, true);

            custom.referenceType = ptrToStructure;
            custom.valueType = 44;

            Custom returnedStruct = structureReturn(custom);
            Marshal.FreeCoTaskMem(ptrToStructure);

            returnedStruct = (Custom)Marshal.PtrToStructure(returnedStruct.referenceType, typeof(Custom));
            Console.WriteLine(returnedStruct.valueType); // Here 'm receiving 12 instead of 44
        }

        [return:MarshalAs(UnmanagedType.I4)]
        [DllImport("CPlusPlus part.dll")]
        public static extern int foo(Custom param);

        // [return:MarshalAs(UnmanagedType.Struct)]
        [DllImport("CPlusPlus part.dll")]
        public static extern Custom structureReturn(Custom param);
    }

    [StructLayout(LayoutKind.Sequential)]
    struct Custom
    {
        [MarshalAs(UnmanagedType.I4)]
        public int valueType;
        public IntPtr referenceType;
    }
}

And C++ part:

typedef struct Custom CUSTOM;
extern "C"
{
    struct Custom
    {
       int valueType;
       Custom* referenceType;
    } Custom;

    _declspec(dllexport) int foo(CUSTOM param)
    {
      return param.referenceType->valueType;
    }

    _declspec(dllexport) CUSTOM structureReturn(CUSTOM param)
    {
      return param;
    }
}

Why I'm receiving 12 instead of 44 in returnedStruct.valueType?

Upvotes: 4

Views: 1134

Answers (1)

cdhowie
cdhowie

Reputation: 168988

You have two errors here:

Semantically, you are setting custom.valueType = 44 but on return of the structure, you are checking custom.referenceType->valueType, which should not be 44 -- it should be 0.

The second error is that you are calling Marshal.FreeCoTaskMem() on this pointer (custom.referenceType) before you unmarshal it! Which means that you are unmarshalling unallocated memory into your Custom struct. At this point, this is undefined behavior, and an answer of 12 is just as valid an outcome as receiving an access violation.


To fix the first problem, you either need to check returnedStruct.valueType without unmarshalling returnedStruct.referenceType, or you need to set childStruct.valueType to 44 before you marshal it into ptrToStructure.

To fix the second problem, you need to reverse the order in which you call Marshal.PtrToStructure() and Marshal.FreeCoTaskMem().

Upvotes: 4

Related Questions