C++中这样声明的DLL函数,在C#中怎么调用啊?

时间:2022-09-01 17:57:57
C++中这样声明的DLL函数,在C#中怎么调用啊?

typedef struct tagTrimInformation
{
  int           p_trim_count;
  LPRECT        p_trim_areas;
  HBITMAP       *p_trim_image;
} TRIMINFORMATION;
typedef TRIMINFORMATION FAR* LPTRIMINFORMATION;

int WINAPI TrimImage
            ( HBITMAP             v_hbitmap, 
              HPALETTE            v_color_palette,
              TRIMINFORMATION     &v_trim_information
            );

22 个解决方案

#1


这样转换:

[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct TRIMINFORMATION{
  public int    p_trim_count;
  public IntPtr p_trim_areas;
  public IntPtr p_trim_image;
}

[ DllImport( "xxx.dll" ,CharSet = CharSet.Ansi)]
public static extern int TrimImage(IntPtr v_hbitmap,IntPtr v_color_palette,ref TRIMINFORMATION v_trim_information);

#2


引用 1 楼 sdl2005lyx 的回复:
这样转换:

C# code


[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct TRIMINFORMATION{
  public int    p_trim_count;
  public IntPtr p_trim_areas;
  public IntPtr p_tr……


要传这个 LPRECT 类型的数组应该怎么传阿? 

#3


我以为这么简单的问题只有我不会呢!!!!!!

#4


不明白你这个api是干什么的. 不过介绍c# 关于api的资料的确很少
vb尼玛超级多 不公平 啊 不公平

#5


引用 4 楼 wy811007 的回复:
不明白你这个api是干什么的. 不过介绍c# 关于api的资料的确很少
vb尼玛超级多 不公平 啊 不公平


这是我们用C++自己写的一个API,我就想知道怎么在C#中调用!!

#6


引用 4 楼 wy811007 的回复:
不明白你这个api是干什么的. 不过介绍c# 关于api的资料的确很少
vb尼玛超级多 不公平 啊 不公平


这是我们自己用C++写的API,我就想知道怎么在C#中调用!!!

#7


引用 4 楼 wy811007 的回复:
不明白你这个api是干什么的. 不过介绍c# 关于api的资料的确很少
vb尼玛超级多 不公平 啊 不公平


这个API是我们自己用C++写的。我现在想知道怎么在C#中调用它!

#8


楼主,这个问题,看似简单,其实复杂!NET提供的互操作技术远比想象中复杂和晦涩!有时一个小问题,你可能几天都搞不定!

花了近两个时,并测试了,给你完整的例子:

C++部分:

struct TrimInfo
{
int count;  //数组成员个数
LPRECT pTrim;  //这里是RECT数组
};

extern "C" void __declspec(dllexport) TestTrim(TrimInfo* pInfo);


C#部分:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct TrimInfo
    {
        public int count;
        public IntPtr pTrim;
    };

   [DllImport(@"E:\pvcs\utscada\Debug\ExamDll.dll", EntryPoint = "TestTrim")]
   public static extern bool TestTrim(ref TrimInfo info);

        private void Form1_Load(object sender, EventArgs e)
        {
            RECT[] rcArr = new RECT[2];
            for(int i=0;i<rcArr.Length;i++)
            {
                rcArr[i].left = i + 1;
                rcArr[i].top = i + 1;
                rcArr[i].right = i + 1;
                rcArr[i].bottom = i + 1;

            }
            ptr=MashalStructArrayToPtr<RECT>(rcArr);
            TrimInfo info=new TrimInfo();
            info.count=2;
            info.pTrim=ptr;
            TestTrim(ref info);
        }

        public static IntPtr MashalStructArrayToPtr<T>(T[] StructArray)
        {
            List<byte[]> byteArrayList = new List<byte[]>();
            // T[] ArrayA = new T[StructArray.Length];
            for (int i = 0; i < StructArray.Length; i++)
            {
                byte[] buf = StructToBytes(StructArray[i]);
                byteArrayList.Add(buf);
            }

            int totalLength = 0;
            // 获取在内存存放的总长度
            for (int i = 0; i < byteArrayList.Count; i++)
            {
                totalLength += byteArrayList[i].Length;
            }

            // 开辟一个Byte 数组
            byte[] allBuffuer = new byte[totalLength];
            int startIndex = 0;
            // 然后依次把byteArrayList的数据拷贝到allBuffuer
            for (int i = 0; i < byteArrayList.Count; i++)
            {
                byteArrayList[i].CopyTo(allBuffuer, startIndex);
                startIndex = startIndex + byteArrayList[i].Length;
            }
            //指针所需的内存空间
            IntPtr BufferPtr = Marshal.AllocCoTaskMem(totalLength);
            //把C#结构体数组拷贝至这个指针
            Marshal.Copy(allBuffuer, 0, BufferPtr, totalLength);
            return BufferPtr;
        }
        public static Byte[] StructToBytes(Object structure)
        {
            Int32 size = Marshal.SizeOf(structure);
            IntPtr buffer = Marshal.AllocHGlobal(size);

            try
            {
                Marshal.StructureToPtr(structure, buffer, false);
                Byte[] bytes = new Byte[size];
                Marshal.Copy(buffer, bytes, 0, size);
                return bytes;
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        }


你自己可以拿我的源码测试,看看是不是得到正确结果。。。

#9


还有一种简单的办法:参数分解!这需要开发者在C++那边做技术处理,也就要求具备一定的C/C++的开发能力!

C++:

extern "C" void __declspec(dllexport) TestTrim2(LPRECT pTrim,int count);

void TestTrim2(LPRECT pTrim,int count)
{
TrimInfo info;

info.count=count;
info.pTrim=pTrim;

TestTrim(&info);
}


C#:

        [DllImport(@"E:\pvcs\utscada\Debug\ExamDll.dll", EntryPoint = "TestTrim2")]
        public static extern void TestTrim2([In, Out]RECT[] rcArr, int count);

        private void Form1_Load(object sender, EventArgs e)
        {
            RECT[] rcArr = new RECT[2];
            for(int i=0;i<rcArr.Length;i++)
            {
                rcArr[i].left = i + 1;
                rcArr[i].top = i + 1;
                rcArr[i].right = i + 1;
                rcArr[i].bottom = i + 1;

            }
            TestTrim2(rcArr, rcArr.Length);   //换成的新的接口
        }


这种技术:就是通常所说的结构体“扁平化”,是参数传递变得简单。

#10


引用 8 楼 sdl2005lyx 的回复:
楼主,这个问题,看似简单,其实复杂!NET提供的互操作技术远比想象中复杂和晦涩!有时一个小问题,你可能几天都搞不定!

花了近两个时,并测试了,给你完整的例子:

C++部分:

C/C++ code


struct TrimInfo

{
    int    count;  //数组成员个数
    LPRECT    pTrim;  //这里是RECT数组
};
……


非常感谢你,我试试嫩故不能达到我的要求,
而且我还有一个问题。函数运行完,这个结构的*p_trim_image指向返回的一个HBITMAP数组,应该怎么使用呢?
typedef struct tagTrimInformation
{
  int           p_trim_count;
  LPRECT        p_trim_areas;
  HBITMAP       *p_trim_image;
} TRIMINFORMATION;



#11


这个结构体:

[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct TRIMINFORMATION{
  public int    p_trim_count;
  public IntPtr p_trim_areas;
  public IntPtr p_trim_image;
}


这个:  public IntPtr p_trim_image;中的p_trim_image,对应的是IntPtr数组。
得到返回值后,根据我给你的方法,反过来处理:


IntPtr[]  ptrArr=new IntPtr[p_trim_count];//个数与结构体的保持一致
Marshal.Copy(p_trim_image,ptrArr,0,p_trim_count);
foreach(IntPtr ptr in ptrArr)
{
     //这里处理每一个ptr 
}


强调一下:如果你复杂的结构体和函数不好调通,你先自己写个简单些的结构体和函数调试。
你把调通了,就打通了一条路!对平台调用、互操作技术有直观的认识。然后逐步搞复杂的,
就会能较快定位问题。

#12


public string 参数名(string 参数)
{
//语句
}

#13


引用 11 楼 sdl2005lyx 的回复:
这个结构体:

C# code


[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct TRIMINFORMATION{
  public int    p_trim_count;
  public IntPtr p_trim_areas;
  public IntPtr p_t……


我对互操作没接触过,一窍不通。我还有两个小问题。
1.非托管DLL分配的空间是否学要C#释放?怎么释放?
2.怎么把IntPtr强制转换成BitMap?


真的非常非常感谢你的回答,连我的同事都说你这人真好。呵呵


#14


1、这个问题涉及到较多方面,要叙述清楚,非一两句能说清楚的。
先简明扼要说说:C、C++、COM如何分配内存,直接影响net这边的释放!
非托管代码三种内存分配、释放方式:
     malloc          free
     new             delete
     CoTaskMemAlloc  CoTaskMemFree

内存分配、释放必须成对使用,否则也会造成内存泄露!net互操作默认是方式是CoTaskMemAlloc,其垃圾回收自动调用的是CoTaskMemFree,也就是说,如果非托管代码采用COM方式分配内存,可以不用显示释放内存,net帮你搞定!而其他两种,不止net支持的方式,必须还是由非托管方来释放!

2、不需再转换,直接使用IntPtr:
   Bitmap bmp=Image.FromHbitmap(ptr);
   

#15


引用 14 楼 sdl2005lyx 的回复:
1、这个问题涉及到较多方面,要叙述清楚,非一两句能说清楚的。
先简明扼要说说:C、C++、COM如何分配内存,直接影响net这边的释放!
非托管代码三种内存分配、释放方式:
  malloc free
  new delete
  CoTaskMemAlloc CoTaskMemFree

内存分配、释放必须成对使用,否则也会造成内存泄露!net互操作默认是方式是CoTaskMemA……


真是太感谢你了!!!

#16


引用 14 楼 sdl2005lyx 的回复:
1、这个问题涉及到较多方面,要叙述清楚,非一两句能说清楚的。
先简明扼要说说:C、C++、COM如何分配内存,直接影响net这边的释放!
非托管代码三种内存分配、释放方式:
  malloc free
  new delete
  CoTaskMemAlloc CoTaskMemFree

内存分配、释放必须成对使用,否则也会造成内存泄露!net互操作默认是方式是CoTaskMemA……


你是CSDN最热心的人!!

#17


 v_trim_information.p_trim_image = (HBITMAP *)GlobalAlloc(GPTR, sizeof(HBITMAP) * v_trim_information.p_trim_count);

我看C++ 这边是这么分配内存的,需要释放么?

#18


引用 2 楼 zengjd 的回复:
引用 1 楼 sdl2005lyx 的回复:
这样转换:

C# code


[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct TRIMINFORMATION{
public int p_trim_count;
public IntPtr p_trim_areas;
pub……


IntPtr.Zero

#19


成对使用,必须用非托管代码释放:GlobalFree(ptr),也就是:你要做C++和C#两边都封装一个相应的释放函数!

#20


引用 19 楼 sdl2005lyx 的回复:
成对使用,必须用非托管代码释放:GlobalFree(ptr),也就是:你要做C++和C#两边都封装一个相应的释放函数!


非常感谢你!!

#21


这个帖子再放几个小时再结贴,让大家学学!
学习技术的同时,也学学助人为乐的精神。

#22


Import[dll]

#1


这样转换:

[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct TRIMINFORMATION{
  public int    p_trim_count;
  public IntPtr p_trim_areas;
  public IntPtr p_trim_image;
}

[ DllImport( "xxx.dll" ,CharSet = CharSet.Ansi)]
public static extern int TrimImage(IntPtr v_hbitmap,IntPtr v_color_palette,ref TRIMINFORMATION v_trim_information);

#2


引用 1 楼 sdl2005lyx 的回复:
这样转换:

C# code


[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct TRIMINFORMATION{
  public int    p_trim_count;
  public IntPtr p_trim_areas;
  public IntPtr p_tr……


要传这个 LPRECT 类型的数组应该怎么传阿? 

#3


我以为这么简单的问题只有我不会呢!!!!!!

#4


不明白你这个api是干什么的. 不过介绍c# 关于api的资料的确很少
vb尼玛超级多 不公平 啊 不公平

#5


引用 4 楼 wy811007 的回复:
不明白你这个api是干什么的. 不过介绍c# 关于api的资料的确很少
vb尼玛超级多 不公平 啊 不公平


这是我们用C++自己写的一个API,我就想知道怎么在C#中调用!!

#6


引用 4 楼 wy811007 的回复:
不明白你这个api是干什么的. 不过介绍c# 关于api的资料的确很少
vb尼玛超级多 不公平 啊 不公平


这是我们自己用C++写的API,我就想知道怎么在C#中调用!!!

#7


引用 4 楼 wy811007 的回复:
不明白你这个api是干什么的. 不过介绍c# 关于api的资料的确很少
vb尼玛超级多 不公平 啊 不公平


这个API是我们自己用C++写的。我现在想知道怎么在C#中调用它!

#8


楼主,这个问题,看似简单,其实复杂!NET提供的互操作技术远比想象中复杂和晦涩!有时一个小问题,你可能几天都搞不定!

花了近两个时,并测试了,给你完整的例子:

C++部分:

struct TrimInfo
{
int count;  //数组成员个数
LPRECT pTrim;  //这里是RECT数组
};

extern "C" void __declspec(dllexport) TestTrim(TrimInfo* pInfo);


C#部分:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct TrimInfo
    {
        public int count;
        public IntPtr pTrim;
    };

   [DllImport(@"E:\pvcs\utscada\Debug\ExamDll.dll", EntryPoint = "TestTrim")]
   public static extern bool TestTrim(ref TrimInfo info);

        private void Form1_Load(object sender, EventArgs e)
        {
            RECT[] rcArr = new RECT[2];
            for(int i=0;i<rcArr.Length;i++)
            {
                rcArr[i].left = i + 1;
                rcArr[i].top = i + 1;
                rcArr[i].right = i + 1;
                rcArr[i].bottom = i + 1;

            }
            ptr=MashalStructArrayToPtr<RECT>(rcArr);
            TrimInfo info=new TrimInfo();
            info.count=2;
            info.pTrim=ptr;
            TestTrim(ref info);
        }

        public static IntPtr MashalStructArrayToPtr<T>(T[] StructArray)
        {
            List<byte[]> byteArrayList = new List<byte[]>();
            // T[] ArrayA = new T[StructArray.Length];
            for (int i = 0; i < StructArray.Length; i++)
            {
                byte[] buf = StructToBytes(StructArray[i]);
                byteArrayList.Add(buf);
            }

            int totalLength = 0;
            // 获取在内存存放的总长度
            for (int i = 0; i < byteArrayList.Count; i++)
            {
                totalLength += byteArrayList[i].Length;
            }

            // 开辟一个Byte 数组
            byte[] allBuffuer = new byte[totalLength];
            int startIndex = 0;
            // 然后依次把byteArrayList的数据拷贝到allBuffuer
            for (int i = 0; i < byteArrayList.Count; i++)
            {
                byteArrayList[i].CopyTo(allBuffuer, startIndex);
                startIndex = startIndex + byteArrayList[i].Length;
            }
            //指针所需的内存空间
            IntPtr BufferPtr = Marshal.AllocCoTaskMem(totalLength);
            //把C#结构体数组拷贝至这个指针
            Marshal.Copy(allBuffuer, 0, BufferPtr, totalLength);
            return BufferPtr;
        }
        public static Byte[] StructToBytes(Object structure)
        {
            Int32 size = Marshal.SizeOf(structure);
            IntPtr buffer = Marshal.AllocHGlobal(size);

            try
            {
                Marshal.StructureToPtr(structure, buffer, false);
                Byte[] bytes = new Byte[size];
                Marshal.Copy(buffer, bytes, 0, size);
                return bytes;
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        }


你自己可以拿我的源码测试,看看是不是得到正确结果。。。

#9


还有一种简单的办法:参数分解!这需要开发者在C++那边做技术处理,也就要求具备一定的C/C++的开发能力!

C++:

extern "C" void __declspec(dllexport) TestTrim2(LPRECT pTrim,int count);

void TestTrim2(LPRECT pTrim,int count)
{
TrimInfo info;

info.count=count;
info.pTrim=pTrim;

TestTrim(&info);
}


C#:

        [DllImport(@"E:\pvcs\utscada\Debug\ExamDll.dll", EntryPoint = "TestTrim2")]
        public static extern void TestTrim2([In, Out]RECT[] rcArr, int count);

        private void Form1_Load(object sender, EventArgs e)
        {
            RECT[] rcArr = new RECT[2];
            for(int i=0;i<rcArr.Length;i++)
            {
                rcArr[i].left = i + 1;
                rcArr[i].top = i + 1;
                rcArr[i].right = i + 1;
                rcArr[i].bottom = i + 1;

            }
            TestTrim2(rcArr, rcArr.Length);   //换成的新的接口
        }


这种技术:就是通常所说的结构体“扁平化”,是参数传递变得简单。

#10


引用 8 楼 sdl2005lyx 的回复:
楼主,这个问题,看似简单,其实复杂!NET提供的互操作技术远比想象中复杂和晦涩!有时一个小问题,你可能几天都搞不定!

花了近两个时,并测试了,给你完整的例子:

C++部分:

C/C++ code


struct TrimInfo

{
    int    count;  //数组成员个数
    LPRECT    pTrim;  //这里是RECT数组
};
……


非常感谢你,我试试嫩故不能达到我的要求,
而且我还有一个问题。函数运行完,这个结构的*p_trim_image指向返回的一个HBITMAP数组,应该怎么使用呢?
typedef struct tagTrimInformation
{
  int           p_trim_count;
  LPRECT        p_trim_areas;
  HBITMAP       *p_trim_image;
} TRIMINFORMATION;



#11


这个结构体:

[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct TRIMINFORMATION{
  public int    p_trim_count;
  public IntPtr p_trim_areas;
  public IntPtr p_trim_image;
}


这个:  public IntPtr p_trim_image;中的p_trim_image,对应的是IntPtr数组。
得到返回值后,根据我给你的方法,反过来处理:


IntPtr[]  ptrArr=new IntPtr[p_trim_count];//个数与结构体的保持一致
Marshal.Copy(p_trim_image,ptrArr,0,p_trim_count);
foreach(IntPtr ptr in ptrArr)
{
     //这里处理每一个ptr 
}


强调一下:如果你复杂的结构体和函数不好调通,你先自己写个简单些的结构体和函数调试。
你把调通了,就打通了一条路!对平台调用、互操作技术有直观的认识。然后逐步搞复杂的,
就会能较快定位问题。

#12


public string 参数名(string 参数)
{
//语句
}

#13


引用 11 楼 sdl2005lyx 的回复:
这个结构体:

C# code


[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct TRIMINFORMATION{
  public int    p_trim_count;
  public IntPtr p_trim_areas;
  public IntPtr p_t……


我对互操作没接触过,一窍不通。我还有两个小问题。
1.非托管DLL分配的空间是否学要C#释放?怎么释放?
2.怎么把IntPtr强制转换成BitMap?


真的非常非常感谢你的回答,连我的同事都说你这人真好。呵呵


#14


1、这个问题涉及到较多方面,要叙述清楚,非一两句能说清楚的。
先简明扼要说说:C、C++、COM如何分配内存,直接影响net这边的释放!
非托管代码三种内存分配、释放方式:
     malloc          free
     new             delete
     CoTaskMemAlloc  CoTaskMemFree

内存分配、释放必须成对使用,否则也会造成内存泄露!net互操作默认是方式是CoTaskMemAlloc,其垃圾回收自动调用的是CoTaskMemFree,也就是说,如果非托管代码采用COM方式分配内存,可以不用显示释放内存,net帮你搞定!而其他两种,不止net支持的方式,必须还是由非托管方来释放!

2、不需再转换,直接使用IntPtr:
   Bitmap bmp=Image.FromHbitmap(ptr);
   

#15


引用 14 楼 sdl2005lyx 的回复:
1、这个问题涉及到较多方面,要叙述清楚,非一两句能说清楚的。
先简明扼要说说:C、C++、COM如何分配内存,直接影响net这边的释放!
非托管代码三种内存分配、释放方式:
  malloc free
  new delete
  CoTaskMemAlloc CoTaskMemFree

内存分配、释放必须成对使用,否则也会造成内存泄露!net互操作默认是方式是CoTaskMemA……


真是太感谢你了!!!

#16


引用 14 楼 sdl2005lyx 的回复:
1、这个问题涉及到较多方面,要叙述清楚,非一两句能说清楚的。
先简明扼要说说:C、C++、COM如何分配内存,直接影响net这边的释放!
非托管代码三种内存分配、释放方式:
  malloc free
  new delete
  CoTaskMemAlloc CoTaskMemFree

内存分配、释放必须成对使用,否则也会造成内存泄露!net互操作默认是方式是CoTaskMemA……


你是CSDN最热心的人!!

#17


 v_trim_information.p_trim_image = (HBITMAP *)GlobalAlloc(GPTR, sizeof(HBITMAP) * v_trim_information.p_trim_count);

我看C++ 这边是这么分配内存的,需要释放么?

#18


引用 2 楼 zengjd 的回复:
引用 1 楼 sdl2005lyx 的回复:
这样转换:

C# code


[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct TRIMINFORMATION{
public int p_trim_count;
public IntPtr p_trim_areas;
pub……


IntPtr.Zero

#19


成对使用,必须用非托管代码释放:GlobalFree(ptr),也就是:你要做C++和C#两边都封装一个相应的释放函数!

#20


引用 19 楼 sdl2005lyx 的回复:
成对使用,必须用非托管代码释放:GlobalFree(ptr),也就是:你要做C++和C#两边都封装一个相应的释放函数!


非常感谢你!!

#21


这个帖子再放几个小时再结贴,让大家学学!
学习技术的同时,也学学助人为乐的精神。

#22


Import[dll]