永发信息网

C#和C C++调用dll变量转换问题,双指针?

答案:3  悬赏:60  手机版
解决时间 2021-02-23 21:23
C结构体定义:
typedef struct _IntImage
{
int height; //图像的高
int width; //宽
int **data; //data:图像二维指针,用法为char** data = new char*[height];
//for (int i=0; i < height; i++)
//data[i] = &(buf[i*width]);
int *buf; //buf:图像数据区,按行存储,buf[0] —buf[width*height]
int variance;
int label;
int bFlip;

}IntImage;
我在C#里定义结构体为:
[DllImport("FaceLib.dll", EntryPoint = "saveIntImage", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int saveIntImage(string filename, ref IntImage intImg);
[StructLayout(LayoutKind.Sequential)]
public unsafe struct IntImage
{
[MarshalAs(UnmanagedType.I4)]
public int height;
[MarshalAs(UnmanagedType.I4)]
public int width;
[MarshalAs(UnmanagedType.ByValArray)]
public IntPtr[] data;
[MarshalAs(UnmanagedType.SysUInt)]
public IntPtr buf;
[MarshalAs(UnmanagedType.I4)]
public int variance;
[MarshalAs(UnmanagedType.I4)]
public int label;
[MarshalAs(UnmanagedType.I4)]
public int bFlip;
}
不知道定义的是否正确?关键是int** data和int* buf两项的定义我不太清除怎么处理。我用了IntPtr buf指向图像存储的缓冲区,IntPtr [] data指向每行数据的首地址。
C里面的调用函数:
int setIntImage(IntImage* img, int value);

我在c#里面由一个由Bitmap图像转换为上述格式定义的图像的函数:
public static void Convert_to_IntImage2(ref IntImage intImage, Bitmap bmp)
{
int i = 0, j = 0, m = 0, n = 0, k = 0;
intImage.height = bmp.Height;
intImage.width = bmp.Width;

Color c = new Color();
int size = (intImage.width) * (intImage.height);
byte[] buffer = new byte[size];
for (i = 0; i < bmp.Width; i++)
for (j = 0; j < bmp.Height; j++)
{
c = bmp.GetPixel(i, j);
buffer[m] = (byte)((39 * c.R + 11 * c.G + 50 * c.B) / 100);
m++;
}
intImage.buf = Marshal.AllocHGlobal(size);
Marshal.Copy(buffer,0,intImage.buf,size);

intImage.data=new IntPtr[bmp.Width];
for (i = 0; i < bmp.Width; i++)
{
intImage.data[i]= Marshal.UnsafeAddrOfPinnedArrayElement(buffer,i*bmp.Height); //获取指定数组指定索引处的地址
}
}
}

在该转换函数里面,不知道结构体中的buf和data如何分配空间和赋值?才能使得给c里面的函数传递参数?出现“尝试读取或写入受保护的内存,这通常指示其他的内存已破坏”。
请问如何平台调用来传递这些复杂的结构体?
此问题来源于:http://q.cnblogs.com/q/2813/。我也遇到类似问题,baidu和google了很多方法, 都未能解决
最佳答案
[DllImport("FaceLib.dll", EntryPoint = "saveIntImage", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern int saveIntImage(string filename, IntImage* intImg);
[StructLayout(LayoutKind.Sequential)]
public unsafe struct IntImage
{
    public int height;
    public int width;
    public int** data;
    public int* buf;
    public int variance;
    public int label;
    public int bFlip;
    ~IntImage()
    {
        if (data == (int**) 0)
            Marshal.FreeCoTaskMem(new IntPtr(data));
        if (buf == (int*) 0)
            Marshal.FreeCoTaskMem(new IntPtr(buf));
    }
}
public static void Convert_to_IntImage2(ref IntImage intImage, Bitmap bmp)
{
    intImage.height = bmp.Height;
    intImage.width = bmp.Width;
    Color c = new Color();
    int size = (intImage.width) * (intImage.height);
    byte[] buffer = new byte[size];
    int m = 0;
    for (int i = 0; i < bmp.Width; i++)
        for (int j = 0; j < bmp.Height; j++)
        {
            c = bmp.GetPixel(i, j);
            buffer[m] = (byte)((39 * c.R + 11 * c.G + 50 * c.B) / 100);
            m++;
        }
    unsafe
    {
        intImage.buf = (int*) Marshal.AllocCoTaskMem(size * sizeof(int)).ToPointer();
        int* pDst = intImage.buf;
        fixed (byte* pBuf = &buffer[0])
        {
            for (int i = 0; i < size; ++i)
                pDst[i] = pBuf[i];
        }
        intImage.data = (int**)Marshal.AllocCoTaskMem(bmp.Width * sizeof(int*)).ToPointer();
        int** pData = intImage.data;
        int bmpHeight = bmp.Height;
        for (int i = 0; i < bmp.Width; i++)
        {
            pData[i] = &pDst[i * bmpHeight];
        }
    }
}试试看这样



如果不行,尝试从kernel32.dll导入GetProcessHeap, HeapAlloc, HeapFree这三个函数来分配和释放内存。
我没有你那个dll,所以代码其实我没有测试,也许里面包含你需要自己调试的错误
释放内存我写在析构函数里面,但是对于结构体……析构函数这东西真的可以用吗其实我不太清楚。或许你应该显式释放它们。
指针的地方我给直接改成指针了,而不是IntPtr
原来分配的代码有问题,一个长度为x的int数组占用的内存并不是x而是x * sizeof(int),其实就是4x。所以你分配的内存其实不够那个数组使用。
看你c语言的函数声明,没有WINAPI或CALLBACK或APIENTRY之类的修饰,有可能不是stdcall,我给改成cdecl了。参数也直接使用指针了。


其实C#是可以用指针的,直接用指针就好了嘛…………
全部回答
c#调用c++编写的dll函数各种参数传递问题 1. 不返回值的参数 c++ 原型: bool sendnewsms(char *sztel, char *szmessage); c#引用; [dllimport( "cdmacard.dll",entrypoint="sendnewsms")] public static extern bool sendnewsms(string phone,string msg); 2. 带返回值(char *) c++原型: bool getcarderrormessage(char *szerrormessage , int errorcode); c#引用 [dllimport( "cdmacard.dll",entrypoint="getcarderrormessage")] public static extern int getcarderrormessage(stringbuilder msg,int errorcode); stringbuilder buf = new stringbuilder(1024);//指定的buf大小必须大于可能的最大长度 getcarderrormessage(buf,1); 3. 带返回值(其他类型) c++原型: bool getsmssavestation (int *nsmsstation); c#引用 [dllimport( "cdmacard.dll",entrypoint="getsmssavestation")] public static extern bool getsmssavestation(ref int nstation); 4. 传递结构体指针(c++填充) c++原型: struct net_info_struct { dword ndurationtime; //持续时间 double nreceivebyte; //接收字节 double nsendbyte; //发送字节 }; bool netgetconnectdetail(net_info_struct *lpnetinfo); c#引用 public struct net_info_struct { public uint ndurationtime; //持续时间 public double nreceivebyte; //接收字节 public double nsendbyte; //发送字节 } [dllimport( "cdmacard.dll",entrypoint="netgetconnectdetail")] public static extern int netgetconnectdetail(ref net_info_struct pnetinfo); net_info_struct netinfo = new net_info_struct(); netgetconnectdetail(ref netinfo); 5. 传递结构体数组(c++来填充) c++原型: struct uim_book_struct { int uimindex; char szname[15]; char szphone[21]; }; int readuimallbook(uim_book_struct lpuimbookitem[],int nmaxarraysize); c#引用 [structlayout(layoutkind.sequential, charset = charset.ansi)]//可以指定编码类型 public struct uim_book_struct { public int uimindex; [marshalas(unmanagedtype.byvaltstr, sizeconst= 15)] public string szname; [marshalas(unmanagedtype.byvaltstr, sizeconst= 21)] public string szphone; }; [dllimport( "cdmacard.dll",entrypoint="readuimallbook")] public static extern int readuimallbook([out] uim_book_struct [] lpuimbookitem,int nmaxarraysize); uim_book_struct[] p = new uim_book_struct[20]; int ret = readuimallbook(p,p.length); 6. 注意问题 类型不一致,会导致调用失败, (1) long 类型,在c++中是4字节的整数,在c#中是8字节的整数; (2) 字符串类型的设置不正确;
可惜没时间,没细看你的东西,只略扫过; 参考“C#混合编程”、“调用约定”两个主题应该可以解决你的问题
我要举报
如以上问答信息为低俗、色情、不良、暴力、侵权、涉及违法等信息,可以点下面链接进行举报!
大家都在看
张三炕布塑料布批发地址在什么地方,想过去办
艾茗优装饰材料厂地址在哪,我要去那里办事
市场上什么牌子的洗发水pH值4.5-5.5?
机甲旋风心眼怎样练级快啊(我知道心眼刷图不
厌加鬼念什么
【我的超人】求一篇《假如我是超人》作文
玉酩这个地址在什么地方,我要处理点事
飞机可以带眼药水吗
急!!我在建设银行自动存款机存钱时弄成IC卡
我家里无线网老是被蹭网,有什么解决办法没有
亚洲开放(香港)教育学院 是什么大学
五金的冲压模,怎么区分硬料和软料,怎么配导
信心奶粉店地址在哪,我要去那里办事
高仿范思哲拖鞋去哪买,A货价格一双多少钱
佛山市达科广告有限公司在什么地方啊,我要过
推荐资讯
龙江镇岭咀村卫生站地址有知道的么?有点事想
华盛荣超市地址在什么地方,想过去办事
一幅地图的比例是1::120000,说明实际距离是
左岸休闲吧在什么地方啊,我要过去处理事情
笔记本有触电感?
<急>一只40瓦的节能灯和40瓦的日光灯的优缺点
在淘宝网上,两台电脑,两个身份证,两个网线可
出纳一般付款流程是什么样
介绍一下prokids这个牌子
招远电视台我想知道这个在什么地方
福州有学街舞吗?那个团队是最好的?
北京动物园站和动物园(枢纽站)
正方形一边上任一点到这个正方形两条对角线的
阴历怎么看 ?