c# - 与嵌入式动态分配数组的托管/非托管类型组合无效

标签 c# arrays dynamic pinvoke marshalling

我在非托管 Win32 C++ DLL 中有一个通用构造:

// FirstElemPtrContainer.h
#include "stdafx.h"

typedef unsigned char elem_type; // a byte

typedef struct FirstElemPtrContainer {
    unsigned char num_elems;
    void *allocd_ary;
} FirstElemPtrContainer;

结构中的 void* 旨在包含指向已分配字节数组的第一个元素的指针。

使用此定义的 DLL 然后导出函数来分配、填充和释放该结构:

// The exported allocator function.
extern "C" _declspec(dllexport) 
    FirstElemPtrContainer *BuildStruct(int elem_count)
{
    FirstElemPtrContainer *fepc_ptr = new FirstElemPtrContainer;
    fepc_ptr->num_elems = elem_count;
    elem_type *ary = new elem_type[fepc_ptr->num_elems];
    for (int i = 0; i < fepc_ptr->num_elems; i++)
    {
        ary[i] = ((i + 1) * 5); // multiples of 5
    }
    fepc_ptr->allocd_ary = ary;

    return fepc_ptr;
}

// The exported deallocator function.
extern "C" _declspec(dllexport) void 
    DestroyStruct(FirstElemPtrContainer *fepc_ptr)
{
    delete[] fepc_ptr->allocd_ary;
    delete fepc_ptr;
}

这些对于本地调用者来说效果很好。

在 C# 中,我尝试通过 PInvoke 描述相同的结构:

[StructLayout(LayoutKind.Sequential)]
public struct FirstElemPtrContainer
{
    public byte num_elems;
    [MarshalAs(UnmanagedType.LPArray, 
        ArraySubType = UnmanagedType.U1, SizeConst = 4)]
    public IntPtr allocd_ary;
}

...并像这样描述调用接口(interface):

public static class Imports
{
    [DllImport("MyLib", CallingConvention = CallingConvention.Winapi)]
    public static extern IntPtr BuildStruct(int elem_count);

    [DllImport("MyLib", CallingConvention = CallingConvention.Winapi)]
    public static extern void DestroyStruct(IntPtr fepc_ptr);
}

现在我尝试调用我的接口(interface):

class Program
{
    const int NUM_ELEMS = 4;
    static void Main(string[] args)
    {
        IntPtr fepc_ptr = Imports.BuildStruct(NUM_ELEMS);
        if ( fepc_ptr == IntPtr.Zero ) 
        {
            Console.WriteLine("Error getting struct from PInvoke.");
            return;
        }

        FirstElemPtrContainer fepc =
            (FirstElemPtrContainer)Marshal.PtrToStructure(fepc_ptr, 
        typeof(FirstElemPtrContainer));
        //...
    }
}

PtrToStructure() 调用给出错误“无法编码‘MyLibInvoke.FirstElemPtrContainer’类型的字段‘allocd_ary’:托管/非托管类型组合无效(Int/UInt 必须与 SysInt 或 SysUInt 配对)。”

您可以看到我已经硬编码了特定数量的元素,我们假设调用者遵守这些元素。我还添加了一个 ArraySubType 子句,尽管它似乎没有什么区别。为什么会出现类型不匹配的投诉?

最佳答案

你的结构应该这样声明:

[StructLayout(LayoutKind.Sequential)]
public struct FirstElemPtrContainer
{
    public byte num_elems;
    public IntPtr allocd_ary;
}

必须以这种方式完成,因为 allocd_ary 是指向非托管内存的指针,无法由 p/invoke 编码器编码。

为了读取allocd_ary的内容,您可以使用Marshal.Copy .

FirstElemPtrContainer fepc = (FirstElemPtrContainer)Marshal.
    PtrToStructure(fepc_ptr, typeof(FirstElemPtrContainer));
byte[] ary = new byte[fepc.num_elems];
Marshal.Copy(fepc.allocd_ary, ary, 0, ary.Length);

我怀疑 CallingConvention.Winapi 是错误的,您应该使用 CallingConvention.Cdecl

关于c# - 与嵌入式动态分配数组的托管/非托管类型组合无效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10076214/

相关文章:

c# - 返回 Task<bool> 和只返回 bool 有什么区别

c# - 如何从字段中提取类型?

c# - 包含 Linq

c - 根据 C 中的用户输入打印数组

c++ - 在指向对象的指针上调用 delete [] 时出现 BLOCK_TYPE_VALID 错误

c# - asp.net cms 页面子菜单不工作

c - 对带有数组和嵌套 For 循环的程序中特定语句的作用感到困惑

javascript - 用JS对象实现 "join"的最佳方法是什么?

javascript, ajax - 通过 ajax 通过 API 调用更新 HTML 页面

c# - 动态对象,可持久保存到 Azure 并可通过 Dynamic Linq 进行查询