C# 调用 C++ DLL,如何把一个复杂的结构体指针作为参数传过去

远水孤云 2020-05-12 10:29:59
在C# 调用 C++ DLL,如何把一个复杂的结构体指针作为参数传过去:
结构体如下:
struct  TagPoint
{
int x;
int y;
};
struct TagRect
{
int x;
int y;
int width;
int Height;
};
struct TagInfos
{
char* name;
TagPoint* pointList;
TagRect rect;
int pointCount;
};
struct FSize
{
int width;
int height;
int depth;
};
struct TagImageInfos
{
char* path;
TagInfos* tagList;
FSize imageSize;
bool isLable;
int tagCount;
};

    [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct TagPoint
{

/// int
public int x;

/// int
public int y;
}

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct TagRect
{

/// int
public int x;

/// int
public int y;

/// int
public int width;

/// int
public int Height;
}

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct TagInfos
{

/// char*
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
public string name;

/// TagPoint**
public System.IntPtr pointList;

/// TagRect
public TagRect rect;

/// int
public int pointCount;
}

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct FSize
{

/// int
public int width;

/// int
public int height;

/// int
public int depth;
}

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct TagImageInfos
{

/// char*
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
public string path;

/// TagInfos**
public System.IntPtr tagList;

/// FSize
public FSize imageSize;

/// boolean
public bool isLable;

/// int
public int tagCount;
}


要从C#传一个TagImageInfos结构体给C++,目前遇到的问题是结构体里面的指针传值问题
挣扎了好几天.还是没有完全解决,请各位大神帮忙提供个思路或者解决办法,谢谢!

...全文
320 点赞 收藏 8
写回复
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
远水孤云 2020-05-14
引用 7 楼 hfjcloud的回复:
我觉得你的问题是不是因为C++和C#对结构体和对象的内存分配方式不同~ 这样的话两端代码可能不应该一致。用一块结构确定的内存保存一个中间量,完了再把中间量指针传过去,C++这边再解析下套到对应的结构里,直接转的话可能会有问题。 C++端么 再包一个dll吧 可能比较绕~~ 不知道还有没有更好的方法
谢谢!看结果是内存相关的问题,但暂时没有想到直接的解决办法 目前使用的替代办法是C#传一个json字符串给出C++,然后对json字符串进行解析,也能得到想要的数据,这样的话C++端的代码会显得复杂点,但目前只想到它了...
回复
hfjcloud 2020-05-14
我觉得你的问题是不是因为C++和C#对结构体和对象的内存分配方式不同~ 这样的话两端代码可能不应该一致。用一块结构确定的内存保存一个中间量,完了再把中间量指针传过去,C++这边再解析下套到对应的结构里,直接转的话可能会有问题。 C++端么 再包一个dll吧 可能比较绕~~ 不知道还有没有更好的方法
回复
远水孤云 2020-05-12
引用 3 楼 wanghui0380 的回复:
我大概明白你的问题了 你估计是 /// TagPoint** 找个玩意卡住了 这个东西其实是 数组的表达。就是TagPoint[],当然因为俺们没办法确定数组到底多大,所以不能直接定义,所以采用inptr接收 接收后再根据pointCount 得到数组大小。然后再copy对应得字节到内存(pointCount *sizeof(TagPoint))
大神厉害,的确是这里的问题:TagInfos**和TagPoint**这两个点,未知数组大小,就按照指针传递 之前测试的情况也说明下: 1. 传一个TagPoint[](用IntPtr代替)到C++端,C++端用一级指针(TagPoint*)接收,结果不对;C++端用二级指针(TagPoint**)接收,结果就对了; 2. 传一个TagInfos[](用IntPtr代替,)到C++端,C++端TagInfos结构体里面TagPoint定义为二级指针,然后用二级指针(TagInfos**)接收,结果不对;后来请教了一位大神,说是内存不连续的问题(自定义数组转成IntPtr的问题),就按照大神提供的方法处理了一下,结果也ok了 3.现在要传一个TagImageInfos对象给C++端(TagImageInfos结构体里面包含一个TagInfos数组,TagInfos数组里面的每一个元素都包含一个TagPoint数组),无论怎么改目前C++端接收到的结果都是错的 目前的问题应该和第二次测试遇到的问题一样,但是不知道如何处理才能解决,大致情况就是这样
回复
wanghui0380 2020-05-12
如果不是他传给你,是你传给他 其实是 TagPoint[] TagPoints=new TagPoint[20] pointList =Marshal.UnsafeAddrOfPinnedArrayElement(TagPoints,0) 也就是这个数组的第一元素的指针。
回复
wanghui0380 2020-05-12
我大概明白你的问题了 你估计是 /// TagPoint** 找个玩意卡住了 这个东西其实是 数组的表达。就是TagPoint[],当然因为俺们没办法确定数组到底多大,所以不能直接定义,所以采用inptr接收 接收后再根据pointCount 得到数组大小。然后再copy对应得字节到内存(pointCount *sizeof(TagPoint))
回复
远水孤云 2020-05-12
引用 1 楼 wanghui0380 的回复:
我觉着你应该需要一个工具来帮你简化一下工作。 百度“interopsignaturetoolkit ”这是一个工具,虽然工具翻译的多少需要修改一下,不过至少可以简化你80%的工作 另外如果有h头用“swig”也可以,不过swig翻译的东西太笨重,所以一般我也不推荐,除非是有超大量的翻译工作要做,否则还还是建议用interopsignaturetoolkit一条一条翻译来的保险点
感谢提醒,上述C#代码是已经使用了interopsignaturetoolkit 的结果,也是为了保证C++和C#两端代码类型及结构一致
回复
wanghui0380 2020-05-12
我觉着你应该需要一个工具来帮你简化一下工作。 百度“interopsignaturetoolkit ”这是一个工具,虽然工具翻译的多少需要修改一下,不过至少可以简化你80%的工作 另外如果有h头用“swig”也可以,不过swig翻译的东西太笨重,所以一般我也不推荐,除非是有超大量的翻译工作要做,否则还还是建议用interopsignaturetoolkit一条一条翻译来的保险点
回复
远水孤云 2020-05-12
引用 4 楼 wanghui0380 的回复:
如果不是他传给你,是你传给他 其实是 TagPoint[] TagPoints=new TagPoint[20] pointList =Marshal.UnsafeAddrOfPinnedArrayElement(TagPoints,0) 也就是这个数组的第一元素的指针。
大神的这个方法只能解决单纯传TagPoint[](C++ ----->C#),我上面测试的第二个问题的解决方法就是这个,和你的差不多
private IntPtr Cov<T>(T[] ps)
        {
            int ik = ps.Length;
            int len = Marshal.SizeOf(typeof(T));
            IntPtr ptr = Marshal.AllocHGlobal(len * ik);

            IntPtr[] ts = new IntPtr[ik];
            for (int i = 0; i < ik; i++)
            {
                ts[i] = Marshal.UnsafeAddrOfPinnedArrayElement(ps, i);
            }
            Marshal.Copy(ts, 0, ptr, ik);

            return ptr;
        }
但是实际上传的结构体类型还要复杂一点
回复
相关推荐
发帖
C#
创建于2007-09-28

10.5w+

社区成员

.NET技术 C#
申请成为版主
帖子事件
创建了帖子
2020-05-12 10:29
社区公告

让您成为最强悍的C#开发者