P/调用返回带有字符串字段的结构数组

本文关键字:字段 结构 数组 字符串 调用 返回 | 更新日期: 2023-09-27 18:34:18

我使用 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;
}

我的托管代码如下所示:

 [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);

如果我尝试调用该方法,我将跳入非托管代码并可以成功调试它,并得到一个带有DN_OPstructs的数组。但是,如果我读出它的字段,例如.名称或 .Guid,我收到此错误:

(... 0x000007fefd921757的第一次机会例外。exe: 0xC0000005: 访问冲突读取位置0xffffffffffffffff。

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

我试图将"ArraySubType = UnmanagedType.LPStruct"添加到我的方法声明中;它没有帮助。

P/调用返回带有字符串字段的结构数组

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:

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));
}

我也对你的结构中的属性持怀疑态度。为什么你有二传手和吸盘手?看起来数据不像是朝那个方向流动的。您使用的非托管代码显示分配CoTaskMemAllocStringToHGlobalAnsi不匹配。因此,即使我怀疑您是否应该编写设置,因此也许应该删除对StringToHGlobalAnsi的调用,但我也怀疑您正在使用的分配器存在混淆。

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