Chinaunix首页 | 论坛 | 博客
  • 博客访问: 671066
  • 博文数量: 103
  • 博客积分: 2532
  • 博客等级: 大尉
  • 技术积分: 2039
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-26 16:07
文章分类

全部博文(103)

文章存档

2012年(38)

2011年(28)

2010年(16)

2009年(16)

2008年(5)

分类:

2010-12-02 09:40:35

()     动态装载、调用DLL中的非托管函数

在上面已经说明了如何用DllImport调用DLL中的非托管函数,但是这个是全局的函数,假若DLL中的非托管函数有一个静态变量S,每次调用这个函数的时候,静态变量S就自动加1。结果,当需要重新计数时,就不能得出想要的结果。下面将用例子说明:

1.        DLL的创建

1)        启动Visual C++ 6.0

2)        新建一个“Win32 Dynamic-Link Library”工程,工程名称为“Count”

3)        “Dll kind”选择界面中选择“A simple dll project”

4)        打开Count.cpp,添加如下代码:

// 导出函数,使用“ _stdcall ” 标准调用

extern "C" _declspec(dllexport)int _stdcall count(int init);

 

int _stdcall count(int init)

{//count 函数,使用参数 init 初始化静态的整形变量 S ,并使 S 自加 1 后返回该值

static int S=init;

S++;

return S;

}

5)        “F7”进行编译,得到Count.dll(在工程目录下的Debug文件夹中)。

 

2.         DllImport调用DLL中的count函数

1)        打开项目“Tzb”,向“Form1”窗体中添加一个按钮。

2)        改变按钮的属性:Name “B2”TextDllImport调用DLLcount函数,并将按钮B1调整到适当大小,移到适当位置。

3)        打开“Form1cs”代码视图,使用关键字 static extern 声明方法“count”,并使其具有来自 Count.dll 的导出函数count的实现,代码如下:

 

[DllImport("Count.dll")]

static extern int count(int init);

4)        “Form1cs[设计]视图中双击按钮B2,在“B2_Click”方法体内添加如下代码:

MessageBox.Show(" DllImport 调用 DLL 中的 count 函数, \n 传入的实参为 0 ,得到的结果是: "+count(0).ToString()," 挑战杯 ");

MessageBox.Show(" DllImport 调用 DLL 中的 count 函数, \n 传入的实参为 10 ,得到的结果是: "+count(10).ToString()+"\n 结果可不是想要的 11 哦!!! "," 挑战杯 ");

MessageBox.Show(" 所得结果表明: \n DllImport 调用 DLL 中的非托管 \n 函数是全局的、静态的函数!!! "," 挑战杯 ");

 

5)        Count.dll复制到项目“Tzb”bin\Debug文件夹中,按“F5”运行该程序,并点击按钮B2,便弹出如下三个提示框:

 

1个提示框显示的是调用“count(0)”的结果,第2个提示框显示的是调用“count(10)”的结果,由所得结果可以证明DllImport调用DLL中的非托管函数是全局的、静态的函数。所以,有时候并不能达到我们目的,因此我们需要使用下面所介绍的方法:C#动态调用DLL中的函数。

 

   

3.        C#动态调用DLL中的函数

因为C#中使用DllImport是不能像动态load/unload assembly那样,所以只能借助API函数了。在kernel32.dll中,与动态库调用有关的函数包括[3]

LoadLibrary(或MFC AfxLoadLibrary),装载动态库。

GetProcAddress,获取要引入的函数,将符号名或标识号转换为DLL内部地址。

FreeLibrary(或MFCAfxFreeLibrary),释放动态链接库。

它们的原型分别是:

HMODULE LoadLibrary(LPCTSTR lpFileName);

FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName);

BOOL FreeLibrary(HMODULE hModule);

 

现在,我们可以用IntPtr hModule=LoadLibrary(“Count.dll”);来获得Dll的句柄,IntPtr farProc=GetProcAddress(hModule,”_count@4”);来获得函数的入口地址。

但是,知道函数的入口地址后,怎样调用这个函数呢?因为在C#中是没有函数指针的,没有像C++那样的函数指针调用方式来调用函数,所以我们得借助其它方法。经过研究,发现我们可以通过结合使用System.Reflection.EmitSystem.Reflection.Assembly里的类和函数达到我们的目的。为了以后使用方便及实现代码的复用,我们可以编写一个类。

1)        dld类的编写:

1.       打开项目“Tzb”,打开类视图,右击“Tzb”,选择添加”-->“,类名设置为“dld”,即dynamic loading dll 的每个单词的开头字母。

2.       添加所需的命名空间及声明参数传递方式枚举:

using System.Runtime.InteropServices; // DllImport 需用此 命名空间

using System.Reflection; // 使用 Assembly 类需用此 命名空间

using System.Reflection.Emit; // 使用 ILGenerator 需用此 命名空间

 

          “public class dld”上面添加如下代码声明参数传递方式枚举:

///

/// 参数传递方式枚举 ,ByValue 表示值传递 ,ByRef 表示址传递

///

public enum ModePass

{

ByValue = 0x0001,

ByRef = 0x0002

}

 

3.       声明LoadLibraryGetProcAddressFreeLibrary及私有变量hModulefarProc

///

/// 原型是 :HMODULE LoadLibrary(LPCTSTR lpFileName);

///

/// DLL 文件名

/// 函数库模块的句柄

[DllImport("kernel32.dll")]

static extern IntPtr LoadLibrary(string lpFileName);

///

/// 原型是 : FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName);

///

/// 包含需调用函数的函数库模块的句柄

/// 调用函数的名称

/// 函数指针

[DllImport("kernel32.dll")]

static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

///

/// 原型是 : BOOL FreeLibrary(HMODULE hModule);

///

/// 需释放的函数库模块的句柄

/// 是否已释放指定的 Dll

[DllImport("kernel32",EntryPoint="FreeLibrary",SetLastError=true)]

static extern bool FreeLibrary(IntPtr hModule);

///

/// Loadlibrary 返回的函数库模块的句柄

///

private IntPtr hModule=IntPtr.Zero;

///

/// GetProcAddress 返回的函数指针

///

private IntPtr farProc=IntPtr.Zero;

 

4.       添加LoadDll方法,并为了调用时方便,重载了这个方法:

 

///

/// 装载 Dll

///

/// DLL 文件名

public void LoadDll(string lpFileName)

{

hModule=LoadLibrary(lpFileName);

if(hModule==IntPtr.Zero)

throw(new Exception(" 没有找到 :"+lpFileName+"." ));

}

 

          若已有已装载Dll的句柄,可以使用LoadDll方法的第二个版本:

public void LoadDll(IntPtr HMODULE)

{

if(HMODULE==IntPtr.Zero)

throw(new Exception(" 所传入的函数库模块的句柄 HMODULE 为空 ." ));

hModule=HMODULE;

}

 

阅读(1468) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~