Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

P/Invoke Returning Array of Structs with string fields

i use p/invoke to return an array of "DN_OPstruct"s from my unmanaged code:

struct DN_OPstruct {
    const char* TargetNode_Identifier;
    const char* Name;
    int TargetNode_NamespaceIndex;
    ...
};


EXTERN_C UA_EXPORT_WRAPPER_IMPORT int getOpToArr(const char* _rootGuid, DN_OPstruct ** array, int * arraySizeInElements){           
    std::list<UA_Ref_and_TargetNode> uaList;
    uaList = getLisT(...) 
    *arraySizeInElements = uaList.size();
    int bytesToAlloc = sizeof(DN_OPstruct) * (*arraySizeInElements);
    DN_OPstruct * a = static_cast<DN_OPstruct*>(CoTaskMemAlloc(bytesToAlloc));
    *array = a;

    for (UA_Ref_and_TargetNode &i: uaList){             
            DN_OPstruct iterOp;     
            iterOp = getOp(...);            
        opList.push_back(iterOp);

    }
    return 1;
}

My managed Code looks like this:

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct DN_OPstruct
    {
        private IntPtr TargetNode_Identifier;
        private IntPtr NamePtr;

        public string Guid
        {
            get { return Marshal.PtrToStringAnsi(TargetNode_Identifier); }
            set { TargetNode_Identifier = Marshal.StringToHGlobalAnsi(value); }
        }

        public string Name
        {
            get { return Marshal.PtrToStringAnsi(NamePtr); }
            set { NamePtr = Marshal.StringToHGlobalAnsi(value); }
        }

        public int TargetNode_NamespaceIndex;
        ...

    };


 [DllImport(@"...", CallingConvention = CallingConvention.Cdecl,
            EntryPoint = "getOpToArr",
            ExactSpelling = true, CharSet = CharSet.Ansi)]
        public static extern int getOpToArr([MarshalAs(UnmanagedType.LPStr)]string myNodeGuid,
            [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] out DN_OPstruct[] array, out int arraySizeInElements);

If i'm trying to call the method, i will jump in the unmanaged code and can debug it through sucessfully and i get an array with my DN_OPstructs back. However, if i read out its fields like .Name or .Guid, i get this error:

First-chance exception at 0x000007fefd921757 in (...).exe: 0xC0000005: Access violation reading location 0xffffffffffffffff.

If there is a handler for this exception, the program may be safely continued.

I tried to add "ArraySubType = UnmanagedType.LPStruct" to my method declaration; it did not help.

like image 526
Pepelee Avatar asked Dec 15 '14 12:12

Pepelee


1 Answers

public static extern int getOpToArr(
    [MarshalAs(UnmanagedType.LPStr)]
    string myNodeGuid,
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] 
    out DN_OPstruct[] array, 
    out int arraySizeInElements
);

The problem is the second parameter. The unmanaged code cannot synthesise a managed .net array. You need to declare the p/invoke like this:

public static extern int getOpToArr(
    string myNodeGuid,
    out IntPtr arrayPtr, 
    out int arrayLen
);

Then you will need to use Marshal.PtrToStructure to marshal the elements of the array to a managed array.

IntPtr arrayPtr;
int arrayLen;
int retval = getOpToArr(nodeGuid, out arrayPtr, out arrayLen);
// check retval

IntPtr ptr = arrayPtr;
DN_OPstruct[] arr = new DN_OPstruct[arrayLen];
for (int i = 0; i < arrayLen; i++)
{
    arr[i] = (DN_OPstruct)Marshal.PtrToStructure(ptr, typeof(DN_OPstruct));
    ptr += Marshal.SizeOf(typeof(DN_OPstruct));
}

I'm also a little sceptical of the properties in your struct. Why do you have setters as well as getters? It doesn't look like the data flows in that direction. And the unmanaged code that you use shows allocation with CoTaskMemAlloc which doesn't match StringToHGlobalAnsi. So even though I doubt that you should be writing settings and so perhaps should remove the calls to StringToHGlobalAnsi, I also suspect there is confusion over the allocator that you are using.

Do note that the code in your question gives no evidence of how you allocated the array which is returned to the caller. So, for all we know, there could be a problem in that part of the code.

like image 81
David Heffernan Avatar answered Nov 14 '22 23:11

David Heffernan