C# P/Invoke返回带有字符串字段的结构数组

C# P/Invoke返回带有字符串字段的结构数组,c#,c++,arrays,struct,pinvoke,C#,C++,Arrays,Struct,Pinvoke,我使用p/invoke从非托管代码返回一个“DN_OPstruct”数组: 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_OPstruc

我使用p/invoke从非托管代码返回一个“DN_OPstruct”数组:

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;
}
如果我试图调用该方法,我将跳入非托管代码并成功地调试它,然后返回一个带有DN_OPstructs的数组。但是,如果读取其字段,如.Name或.Guid,则会出现以下错误:

(…)中0x000007fefd921757处的首次机会异常。exe:0xC0000005: 访问冲突读取位置0xFFFFFFFFFFFFFF

如果存在此异常的处理程序,则程序可能会安全运行 继续

我试图将“ArraySubType=UnmanagedType.LPStruct”添加到我的方法声明中;这没有帮助

public static extern int getOpToArr(
    [MarshalAs(UnmanagedType.LPStr)]
    string myNodeGuid,
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] 
    out DN_OPstruct[] array, 
    out int arraySizeInElements
);
问题是第二个参数。非托管代码无法合成托管.net数组。您需要像下面这样声明p/invoke:

 [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);
public static extern int getOpToArr(
    string myNodeGuid,
    out IntPtr arrayPtr, 
    out int arrayLen
);
然后需要使用
Marshal.PtrToStructure
将数组的元素封送到托管数组

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));
}
IntPtr阵列;
内特阿莱伦;
int-retval=getoptarr(nodeGuid、out-arrayPtr、out-arrayLen);
//支票返还
IntPtr ptr=arrayPtr;
DN_OPstruct[]arr=新DN_OPstruct[arrayLen];
for(int i=0;i
我对结构中的属性也有点怀疑。为什么你们有二传手和传接手?看起来数据不是朝那个方向流动的。您使用的非托管代码显示了与
CoTaskMemAlloc
不匹配的
StringToHGlobalAnsi
的分配。因此,尽管我怀疑您是否应该编写设置,因此可能应该删除对
StringToHGlobalAnsi
的调用,但我也怀疑您正在使用的分配器存在混淆


请注意,问题中的代码没有提供如何分配返回给调用方的数组的证据。所以,据我们所知,代码的这一部分可能有问题

我刚刚花了几个小时试图使p/Invoke在类似的场景中工作。我最终编写了一个C++/CLR包装库。我发现处理C++中的托管代码/非托管代码要容易得多。我只是简单地将所有内容映射到几个CLR类中,然后当您向该项目添加ref时,您可以使用这些类,就好像它们是C#(因为它们被编写为与.NET兼容)一样。我在不同的上下文中使用setter,在其中我给出了从托管到unm的属性。代码。不幸的是,我得到了与您的代码建议相同的错误…我建议您进行一些调试。您没有显示所有代码,因此我们无法知道您是否犯了错误。例如,我只能假设您在非托管代码中正确填充了结构数组。但没有证据表明你做过这些。您还忽略了显示如何调用函数。祝你好运。我很乐意把所有相关的细节都贴给你,但这会打破一条线的模式。不过,感谢您的代码建议,我可以冷静地查找其他可能的错误now@Pepelee我没有提出建议。我告诉过你你的代码中有个错误。我不是建议您停止使用
out DN\u OPstruct[]
。我告诉你,这是错误的。如果我是你,我会撤回C++代码并删除所有的复杂性。只需让它分配一个长度为1或2的数组,并用硬编码的值填充它。