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

转载 2016年12月01日 16:06:08

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

C#动态调用C++编写的DLL函数
动态加载 DLL 需要使用 Windows API 函数: LoadLibrary 、 GetProcAddress 以及 FreeLibrary 。我们可以使用 DllImport 在 C# 中使用这三个函数。

[DllImport("Kernel32")]
public  static  extern  int  GetProcAddress(int handle, String funcname);

[DllImport("Kernel32")]
public  static  extern  int  LoadLibrary(String funcname);

[DllImport("Kernel32")]
public  static  extern  int  FreeLibrary(int handle);

当我们在 C++ 中动态调用 Dll 中的函数时,我们一般的方法是:
假设 DLL 中有一个导出函数,函数原型如下:
BOOL __stdcall foo(Object &object, LPVOID lpReserved);

1 、首先定义相应的函数指针:
typedef BOOL (__stdcall *PFOO)(Object &object, LPVOID lpReserved);

2 、调用 LoadLibrary 加载 dll :
HINSTANCE hInst = ::LoadLibraryW(dllFileName);

3 、调用 GetProcAddress 函数获取要调用函数的地址:
PFOO foo = (PFOO)GetProcAddress(hInst,”foo”);
if (foo == NULL)
{
FreeLibrary(hInst);
retur n false;
}

4 、调用 foo 函数:
BOOL bRet = foo(object,(LPVOID)NULL);

5 、使用完后应释放 DLL :
FreeLibrary(hInst);

那么在 C# 中应该怎么做呢?方法基本上一样,我们使用委托来代替 C++ 的函数指针,通过 .NET Framework 2.0 新增的函数GetDelegateForFunctionPointer 来得到一个委托的实例:

下面封装了一个类,通过该类我们就可以在 C# 中动态调用 Dll 中的函数了:

public  class DLLWrapper
{
    ///
    /// API LoadLibrary
    ///
    [DllImport("Kernel32")]
    public static extern int LoadLibrary(String funcname);

    ///
    /// API GetProcAddress
    ///
    [DllImport("Kernel32")]
    public static extern int GetProcAddress(int handle, String funcname);

    ///
    /// API FreeLibrary
    ///
    [DllImport("Kernel32")]
    public static extern int FreeLibrary(int handle);

    ///
    /// 通过非托管函数名转换为对应的委托 , by jingzhongrong
    ///
    /// 通过 LoadLibrary 获得的 DLL 句柄
    /// 非托管函数名
    /// 对应的委托类型
    /// 委托实例,可强制转换为适当的委托类型
    public static Delegate GetFunctionAddress(int dllModule, String functionName, Type t)
    {
       int address = GetProcAddress(dllModule, functionName);
       if (address == 0)
           return null;
       else
           return Marshal.GetDelegateForFunctionPointer(new intPtr(address), t);
    }

    ///
    /// 将表示函数地址的 intPtr 实例转换成对应的委托 , by jingzhongrong
    ///
    public static Delegate GetDelegateFromintPtr(intPtr address, Type t)
    {
       if (address == intPtr.Zero)
           return null;
       else
           return Marshal.GetDelegateForFunctionPointer(address, t);
    }

    ///
    /// 将表示函数地址的 int  转换成对应的委托,by jingzhongrong
    ///
    public static Delegate GetDelegateFromintPtr(int address, Type t)
    {
       if (address == 0)
           return null;
       else
           return Marshal.GetDelegateForFunctionPointer(new intPtr(address), t);
    }
}

通过这个类,我们这样调用 DLL :

1 、声明相应的委托(正确声明很重要,否则不能调用成功,后面有详细介绍)。

2 、加载 DLL :

int  hModule = DLLWrapper.LoadLibrary(dllFilePath);
if (hModule == 0)
    retur nfalse;

3 、获取相应的委托实例:

FOO foo = (FOO)DLLWrapper.GetFunctionAddress(hModule, "foo", typeof(FOO));
if (foo == null)
{
    DLLWrapper.FreeLibrary(hModule);
    retur nfalse;
}

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)]
    structStructVersionInfo
    {
       public int MajorVersion;
       public int MinorVersion;
    }

另外,如果单独使用内部类型没有另外使用到字符串、结构、类,可以将结构在 C# 中声明为 class :

 [StructLayout(LayoutKind.Sequential)]
    classStructVersionInfo
    {
       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#
    public  StructVersionInfo ver;

C++ :函数指针声明
    PFOO pFoo; // 具体声明见文章前面部分
C#:
    public  int Ptr pFoo;  // 也可以为 public  int  pFoo;
        // 不同的声明方法可以使用上面 DLLWrapper 类的相应函数获取对应的委托实例

如果在结构中使用到了 union ,那么可以使用 FieldOffset 指定具体位置。

3 、委托的声明:

当 C++ 编写的 DLL 函数需要通过指针传出将一个结构:如以下声明:
void getVersionInfo( VERSION_INFO *ver);
对于在 C# 中声明为 class 的结构(当 VERSION_INFO 声明为 class )
delegate void getVersionInfo ( VERSION_INFO ver);
如果结构声明为 struct ,那么应该使用如下声明:
delegate void getVersionInfo ( ref VERSION_INFO ver);
注意:应该使用 ref 关键字。

如果 DLL 函数需要传入一个字符串,比如这样:
BOOL __stdcall jingzhongrong1(constwchar_t* lpFileName, int * FileNum);
那么使用委托来调用函数的时候应该在 C# 中如下声明委托:
delegatebooljingzhongrong1(
[MarshalAs(UnmanagedType.LPWStr)]String FileName,
refint FileNum);
注意:应该使用 [MarshalAs(UnmanagedType.LPWStr)] 和 String 进行声明。

如果要在 DLL 函数中传出一个字符串,比如这样:
void __stdcall jingzhongrong2(
wchar_t* lpFileName, // 要传出的字符串
int * Length);
那么我们如下声明委托:
// 使用委托从非托管函数的参数中传出的字符串,
// 应该这样声明,并在调用前为 String Builder 预备足够的空间
delegatevoidjingzhongrong2(
[MarshalAs(UnmanagedType.LPWStr)] String BuilderlpFileName,
refint Length,
);
在使用函数前,应先为 String Builder 声明足够的空间用于存放字符串:
String Builder fileName = new String Builder(FileNameLength);

C# 中静态调用C++dll 和C# 中动态调用C++dll

C# 中静态调用C++dll 和C# 中动态调用C++dll 在最近的项目中,牵涉到项目源代码保密问题,由于代码是C#写的,容易被反编译,因此决定抽取核心算法部分使用C++编写,C++到目前...
  • u010159842
  • u010159842
  • 2016年06月14日 16:44
  • 1298

C#调用C++DLL,及回调函数、string参数传递的总结

Int型传入: Dll端: extern "C" __declspec(dllexport) int Add(int a, int b) {     return ...
  • xuedingkai
  • xuedingkai
  • 2016年11月29日 18:30
  • 710

C#动态调用C++DLL

1.DLL中函数实现extern "C" __declspec(dllexport) int MultiplyByTen(int numberToMultiply);extrn "C" _declsp...
  • procedurecode
  • procedurecode
  • 2008年04月02日 12:41
  • 7626

C++做一个真正动态链接的DLL的做法

让动态链接库真正的动态其实是一件很麻烦的事情。事实上,可以称得上“动态”的函数一共就只有两种,即全局函数和纯虚函数。所以我们有三种做法: 第一种做法,对于接口,不使用任何类,所有导出函数都用exter...
  • dengrk
  • dengrk
  • 2007年12月16日 21:15
  • 665

c++ DLL相关使用

dll
  • forever917
  • forever917
  • 2016年12月21日 10:09
  • 220

C#动态调用DLL中的函数

一般使用方法 [System.Runtime.InteropServices.DllImport("E:\\Project\\DLL\\XXX.dll", EntryPoint = "OpenCom...
  • xumengmeng_xu
  • xumengmeng_xu
  • 2017年02月21日 11:16
  • 525

C++全局函数的dll,C++动态调用。

1、项目结构 其实很简单,只要将CDLL.c文件的后缀改为.cpp即可。 2、 CDLL.h内容:#ifndef __CDLL_H__ #define __CDLL_H__ extern "...
  • wodownload2
  • wodownload2
  • 2016年12月15日 10:08
  • 331

用VS制作简单dll的过程 C和c++版本

创建dll的过程其实并不难,但是MDN上明确表示对于C++制作的dll支持的不好,因此要使用的dll必须是C语言规范的,于是我就打算用VC6.0来做这个dll。做了整整一下午,不断的查阅资料,终于还是...
  • txpp520
  • txpp520
  • 2017年05月18日 23:54
  • 604

Windows动态链接库DLL的使用

windows程序设计使用动态链接库可以有效的分隔大型项目的模块,DLL里面主要提供函数的调用接口(函数名)供其他的外部引用程序调用,调用者在完全不知道动态链接库中的实现方式的情况下,仍然能根据其提供...
  • dangercheng
  • dangercheng
  • 2012年11月14日 10:34
  • 4729

C#调用C++编写的DLL函数, 以及各种类型的参数传递

1. 如果函数只有传入参数,比如: C/C++ Code Copy Code To Clipboard //C++中的输出函数 int __declspec(dllexport) te...
  • vito13
  • vito13
  • 2017年03月31日 15:30
  • 598
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C#动态调用C++编写的DLL函数
举报原因:
原因补充:

(最多只允许输入30个字)