6

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.

1
  • I have just spent hours trying to make P/Invoke work in a similar scenario. I ended up writing a C++/CLR wrapper library. I found it much easier dealing with managed/unmanaged code from C++. I simply mapped everything into a few CLR classes, then when you add ref to that project you can use those classes as if they are C# (because they are written to be .NET compliant) Commented Dec 15, 2014 at 12:15

1 Answer 1

1
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.

Sign up to request clarification or add additional context in comments.

6 Comments

i use the setters in a different context, in which i give the properties from my managed to unm. code. Unfortunately, i get the same error with your code suggestion...
I suggest that you do some debugging. You didn't show all of the code so we've no way of knowing whether or not you made a mistake. For instance, I can only assume that you populated the array of structs correctly in the unmanaged code. But there's no evidence of you having done any of that. You also neglected to show how you call the function. Good luck.
I would willingly post you all relevant details, but it would break the mould of one SO thread. However, thank you for your code suggestion, i can calmly look for other possible mistakes now
@Pepelee I've not made a suggestion. I've told you about an error in your code. I'm not suggesting that you stop using out DN_OPstruct[]. I'm telling you that is wrong.
If I were you I'd strip back the C++ code and remove all the complexity. Just have it allocate an array of length 1 or 2, and populate it with hard coded values.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.