关闭

C#动态调用C++编写的DLL函数

发表于:2014-10-13 10:46

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:fdyang    来源:51Testing软件测试网采编

  通过这个类,我们这样调用DLL:
  1、声明相应的委托(正确声明很重要,否则不能调用成功,后面有详细介绍)。
  2、加载DLL:
  int hModule = DLLWrapper.LoadLibrary(dllFilePath);
  if (hModule == 0)
  return false;
  3、获取相应的委托实例:
  FOO foo = (FOO)DLLWrapper.GetFunctionAddress(hModule, "foo", typeof(FOO));
  if (foo == null)
  {
  DLLWrapper.FreeLibrary(hModule);
  return false;
  }
  4、调用函数:
  foo(...);
  5、.NET并不能自动释放动态加载的DLL,因此我们在使用完DLL后应该自己释放DLL:
  DLLWrapper.FreeLibrary(hModule);
  下面我们将就委托应如何声明进行相应的讨论,在实际操作过程中,我发现使用DllImport方法和动态调用方法两者在C#中对DLL中函数原型的声明是有些区别的,下面我介绍动态调用中委托的声明:
  1、首先应该注意的是,C++中的类型和C#中类型的对应关系,比如C++中的long应该对应C#中的Int32而不是long,否则将导致调用结果出错。
  2、结构的声明使用StructLayout对结构的相应布局进行设置,具体的请查看MSDN:
  使用LayoutKind指定结构中成员的布局顺序,一般可以使用Sequential:
  [StructLayout(LayoutKind.Sequential)]
  struct StructVersionInfo
  {
  public int MajorVersion;
  public int MinorVersion;
  }
  另外,如果单独使用内部类型没有另外使用到字符串、结构、类,可以将结构在C#中声明为class:
  [StructLayout(LayoutKind.Sequential)]
  class StructVersionInfo
  {
  public int MajorVersion;
  public int MinorVersion;
  }
  对应C++中的声明:
  typedef struct _VERSION_INFO
  {
  int MajorVersion;
  int MinorVersion;
  } VERSION_INFO, *PVERSION_INFO;
  如果结构中使用到了字符串,最好应指定相应的字符集:
  [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
  部分常用的声明对应关系(在结构中):
  C++:字符串数组
  wchar_t Comments[120];
  C#:
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 120)]
  public string Comments;
  C++:结构成员
  VERSION_INFO ver;
  C#
  publicStructVersionInfo ver;
  C++:函数指针声明
  PFOO pFoo; //具体声明见文章前面部分
  C#:
  publicIntPtr pFoo; //也可以为 public int pFoo;
  //不同的声明方法可以使用上面DLLWrapper类的相应函数获取对应的委托实例
  如果在结构中使用到了union,那么可以使用FieldOffset指定具体位置。
  3、委托的声明:
  当C++编写的DLL函数需要通过指针传出将一个结构:如以下声明:
  void getVersionInfo(VERSION_INFO *ver);
  对于在C#中声明为class的结构(当VERSION_INFO声明为class)
  delegate voidgetVersionInfo(VERSION_INFO ver);
  如果结构声明为struct,那么应该使用如下声明:
  delegate voidgetVersionInfo(refVERSION_INFO ver);
  注意:应该使用ref关键字。
  如果DLL函数需要传入一个字符串,比如这样:
  BOOL __stdcall jingzhongrong1(const wchar_t* lpFileName, int* FileNum);
  那么使用委托来调用函数的时候应该在C#中如下声明委托:
  delegate bool jingzhongrong1(
  [MarshalAs(UnmanagedType.LPWStr)]String FileName,
  ref int FileNum);
  注意:应该使用[MarshalAs(UnmanagedType.LPWStr)]和String进行声明。
  如果要在DLL函数中传出一个字符串,比如这样:
  void __stdcall jingzhongrong2(
  wchar_t* lpFileName, //要传出的字符串
  int* Length);
  那么我们如下声明委托:
  //使用委托从非托管函数的参数中传出的字符串,
  //应该这样声明,并在调用前为StringBuilder预备足够的空间
  delegate void jingzhongrong2(
  [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpFileName,
  ref int Length,
  );
  在使用函数前,应先为StringBuilder声明足够的空间用于存放字符串:
  StringBuilder fileName = new StringBuilder(FileNameLength);
22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号