C#调用C++动态库的一些理解

Anne ·
更新时间:2024-11-14
· 875 次阅读

  c#调用c++动态库一般我们这样写   [DllImport("UCamer.dll", CallingConvention = CallingConvention.Winapi)]   public extern static void Disp_Destroy(IntPtr hShow);   DllImport的第一个参数UCamer.dll是动态库dll的路径,此dll放在程序运行的根目录或者c:windows/sytem32下   CallingConvention 参数是c#调用c++的方式 是个枚举 msdn解释如下

  从上面来看Winapi方式是根据系统自动选择调用规约的。 而thisCall是对c++类的调用方法。 所以 一般情况下我们选择Winapi可以了。   c#调用dll另一个难点:数据类型转换   http://wenku.baidu.com/link?url=SihlxtHC-HMcEhq3izpd2bux8rNaKOMTpu8NPqjdYlLSwYSV1CqNJdVbxkaZm7OqaaSTEK-KUJqX5jbtkdpnUZ_38No4tsrgqCsf7Th5dqK   百度文库这篇文章基本把c++与c#的对应数据类型总结完了。但是为什么这里还要说呢   1,百度文库这篇文章,包括大部分度娘的类型转换的资料中 对c++中的返回值类型char[] 都转换成了char[] 没做任何改变。   c++中char占一个字节,assic编码。而c#中的char占2个字节(我是在中文版的vs中测试的)。   如果这样转换会出现问题,很容易发生越界,或者读写到受保护的内存等问题。   解决方法是 把char[] 变成c#中的byte[] ,再用Encoding.Assic.getstring方法转换。   2,关于指针,c++所有的指针 在c#上用Intptr ,问题又来了。假如返回的Intptr是个数组指针,在c#中我们怎么读取数组里面的元素呢?   Marshal.Copy();   Marshal类是c#中专门把非托管内存转换成托管内存的神器,不需要unsafe。 Marshal中copy方法是常用的方法。里面有16个重载。能解决目前你能遇到的大部分问题。   msdn中对各个重载解释如下

 

  我们也可以使用共享内存的方式进行操作。部分代码如下

 

//初始化返回图片的大小等信息 myContext = new VlcControlWpfRendererContext(width, height, System.Windows.Media.PixelFormats.Bgr24); //创建共享内存区域 myBitmapSectionPointer = Win32Interop.CreateFileMapping(new IntPtr(-1), IntPtr.Zero, Win32Interop.PageAccess.ReadWrite, 0, myContext.Size, null); //获取共享内存的首地址 map = Win32Interop.MapViewOfFile(myBitmapSectionPointer, Win32Interop.FileMapAccess.AllAccess, 0, 0, (uint)myContext.Size); //把接收后的图片拷入共享内存区域 Win32Interop.CopyMemory(map, data, myContext.Size); //把共享内存中的数组转换为图片 myBitmap = (InteropBitmap)Imaging.CreateBitmapSourceFromMemorySection(myBitmapSectionPointer, myContext.Width, myContext.Height, myContext.PixelFormat, myContext.Stride, 0);

  这里是共享内存的辅助类  主要是一些api函数   其实Intptr本质也是一个Int,Int在c#中和Int32是一样的。所以基本上指针,Long,int在c#中都是int。只是这样些 方便大家知道他是c++中的什么类型,方便转换而已。   3,c++中的函数指针  与c#中的委托   这是c++中对函数指针的定义   typedef VOID (WINAPI *PUSERCALL)( PUCHAR pData, ULONG Length, PVOID pUserData );   对应c#中的例子如下   public delegate void PUSERCALL(IntPtr pData, uint Length, UInt32 pUserData);

  4,我们知道int是占4个字节的。   下面这个是c++的一个方法   U_CAMER LONG WINAPI CAMER_GetPropery( HANDLE hCamer, _CMRCTL Propery );   假如我们把此函数翻译成c#中的下面这个函数   [DllImport("UCamer.dll", CallingConvention = CallingConvention.Winapi)]   public extern static Uint16 CAMER_GetPropery(IntPtr hCamer, CMRCTL Propery);   我们在c#调用此方法   uint16 m_HiWi_temp = (uint)BCamera.CAMER_GetPropery(m_hCamer, CMRCTL.OUT_SIZE);   发现一个很有趣的问题,此处调用没有问题。也有值,但是他是取的int32中4个字节的2个字节。   我们看原本c++对此函数的调用   *((PULONG)m_HiWi) = *((PULONG)m_Display) = CAMER_GetPropery( m_hCamer, OUT_SIZE );   m_hShow = Disp_Create( m_hWnd, m_HiWi[1], m_HiWi[0], m_nColor, (USERDRAW)((m_ReDrawLine == TRUE) ? DrawLine : NULL), this );   本来CAMER_GetPropery函数只返回了一个long类型。c++中通过指针的转换。把long类型转换成了 pulong,也是ulong的数组。   那c#中怎么我们该怎么调用呢

 

int m_HiWi_temp = BCamera.CAMER_GetPropery(m_hCamer, CMRCTL.OUT_SIZE); byte[] m_byte_HiWi = BitConverter.GetBytes(m_HiWi_temp); byte[] temp1 = new byte[2] { m_byte_HiWi[0], m_byte_HiWi[1] }; byte[] temp2 = new byte[2] { m_byte_HiWi[2], m_byte_HiWi[3] }; int width = BitConverter.ToInt16(temp1, 0); int high = BitConverter.ToInt16(temp2, 0);

  这里举这个例子是说c++有时真的是返回一个int类型,但是在c++中可以轻松把int类型通过指针轻松转换成两个uint16的数组。所以c#中我们再转换的时候一定有注意了。   总结:其实数据类型的转换主要是对数据存储空间的转换,c++中的数据类型占用多大的空间,只要转换成c#中占同等空间的数据类型可以了。只是c#看哪种数据类型在操作相应的操作方便些。

 



动态 动态库 调用

需要 登录 后方可回复, 如果你还没有账号请 注册新账号