c#动态调用DLL_第1页
c#动态调用DLL_第2页
c#动态调用DLL_第3页
c#动态调用DLL_第4页
c#动态调用DLL_第5页
已阅读5页,还剩17页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、c#程序实现动态调用dll 的研究转自断点社区http:/ c#程序实现动态调用dll 的研究摘 要:开发高手 2004 期中的化功大法 将 dll 一文,介绍了如何把一个动态链接库作为一个资源嵌入到可执行文件,在可执行文件运行时,自动从资源中释放出来,通过静态加载延迟实现函数的动态加载,程序退出后实现临时文件的自动删除,从而为解决“dll hell 实现了,在internet 程序,但在某一技术论坛上提起这种设计方法时,有网友提出:“ 这种方法好是好,但就是启动速度太慢” 。这是因为程序启动时实现dll ,这个过程会耗费一定的时间。鉴于此问题,经过思索,提出另一个设计方案:dll释放及其重新

2、加载。本文就是对该设计方案的原理分析及使用c#关键词:,嵌入dll 正 文:一、 dll 与应用程序,即为 “dynamic link library 最重要的组成要素之一,打开windows 文件, windows 模块的形式实现。调用来完成一定操作的函数(中一般称为 “ 方法 ”)dll 也只能被该进程的线程访问,它的句柄可以被调用进程所使用, 而调用进程的句柄也可以被该dll 只有一个实例, 且它的编制与具体的编程语言和编译器都没有关系,所以可以通过dll 函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。时提供的一些优点:1 1) 可以减少在磁盘和物理内存中加载的代码

3、的重复量。这不仅可以大大影响在前台运行的程序,而且可以大大影响其他在windows 2) dll 3) 中的函数需要更新或修复时,部署和安装dll 的链接。此外,如果多个程序使用同一个dll 时,此问题可能会更频繁地出现。二、 dll 的调用的方法都不尽相同,在此只对用c#的方法进行介绍。首先,什么是非托管。一般可以认为:非托管代码主要是基于 win 32,activex 平台开发的。 如果您想深入了解托管与非托管的关系与区别,及它们的运行机制,请您自行查找资料,本文件在此不作讨论。(一) 调用 dll 中的非托管函数一般方法,应该在 c#dllimport( “dll 返回变量类型(参数列表

4、):dll 访问修饰符,除了abstract文件中你需调用方法的返回变量类型。文件中你需调用方法的名称。文件中你需调用方法的列表。:需要在程序声明中使用system.runtime.interopservices dllimportdll所设置的路径)。文件中的定义相一致。属性设置,如:dllimport(user32.dll, entrypoint=messageboxa) static extern int msgbox(int hwnd, string msg, string caption, int type); 属性:charset ;setlasterror ;exactspell

5、ing 是 否 必 须 与 指 示 的 入 口 点 的 拼 写 完 全 匹 配 , 如 : exactspelling=falsepreservesig如 :preservesig=truecallingconvention 如: callingconvention=callingconvention.winapi 。c#1. ,新建一个项目,项目名称为“tzb 应用程序 ” 。2. 窗体 ” 项中双击 “button ”窗体中添加一个按钮。3. 为 “b1”为 “ 调用 dll ,并将按钮b14. ” ,打开 “form1”代码视图,在 “namespace tzb” ,以导入该命名空间。5

6、. cs,在 “b1_click和 extern ”,将 dllimport dll ”函数,具体代码如下:dllimport(user32.dll, entrypoint=messageboxa) static extern int msgbox(int hwnd, string msg, string caption, int type); ” 方法体内添加如下代码,以调用方法“msgbox msgbox(0, 这就是用dllimport 调用 dll 弹出的提示框哦!, 挑战杯,0 x30); 6. ” ,便弹出如下提示框:(二) 动态装载、调用dll 中的非托管函数调用 dll 中的非

7、托管函数有一个静态变量s就自动加 11. dll 的创建1) ;2) 个“win32 dynamic -link libr ary ” ;3) ” 选择界面中选择 “a simple dll project4),添加如下代码:/ 导出函数,使用 “ _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+; ret

8、urn s; 5) ” (在工程目录下的debug 2. 用 dllimport调用 dll 中的 count 函数1) ” ,向“form1 窗体中添加一个按钮。2) 为 ” ,text “用 dllimport中 count 调整到适当大小,移到适当位置。3) cs 和 extern ”,并使其具有来自count.dll 的实现,代码如下:dllimport(count.dll) static extern int count(int init); 4) cs,在 “b2_click messagebox.show( 用 dllimport 调用dll 中的count 函数,n 传入的实参

9、为0 ,得到的结果是:+count(0).tostring(), 挑战杯); messagebox.show( 用 dllimport 调用dll 中的 count 函数,n 传入的实参为10 ,得到的结果是:+count(10).tostring()+n 结果可不是想要的11 哦!, 挑战杯); messagebox.show( 所得结果表明:n 用 dllimport 调用dll 中的非托管n 函数是全局的、 静态的函数!, 挑战杯); 5) 复制到项目 “tzb 文件夹中,按 “f5 运行该程序,并点击按钮b2 个提示框显示的是调用“count(0) 个提示框显示的是调用“count(1

10、0) 调用 dll 动态调用 dll 3. c#动态调用 dll 中的函数中使用dllimport那样,所以只能借助api中,与动态库调用有关的函数包括3 loadlibrary的afxloadlibrary getprocaddress 内部地址。(或 mfc ),释放动态链接库。现在,我们可以用intptr hmodule=loadlibrary( “count.dll”);的句柄 ,”); 中是没有函数指针的,没有像c+及system.reflection.assembly1) dld ” ,打开类视图,右击“tzb“类” ,类名设置为 “dld的每个单词的开头字母。2. using s

11、ystem.runtime.interopservices; / 用 dllimport 需用此命名空间using system.reflection; / 使用assembly 类需用此命名空间using system.reflection.emit; / 使用 ilgenerator 需用此命名空间” 上面添加如下代码声明参数传递方式枚举:/ / 参数传递方式枚举,byvalue 表示值传递,byref 表示址传递/ public enum modepass byvalue = 0 x0001, byref = 0 x0002 3. 、getprocaddress 及私有变量hmodule

12、:/ / 原型是: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 intp

13、tr 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.zer

14、o; / / getprocaddress 返回的函数指针/ private intptr farproc=intptr.zero; 4. 方法,并为了调用时方便,重载了这个方法:/ / 装载 dll / / dll 文件名 public void loaddll(string lpfilename) hmodule=loadlibrary(lpfilename); if(hmodule=intptr.zero) throw(new exception( 没有找到:+lpfilename+. ); 的句柄,可以使用loaddll public void loaddll(intptr hmodu

15、le) if(hmodule=intptr.zero) throw(new exception( 所传入的函数库模块的句柄hmodule 为空 . ); hmodule=hmodule; 5. 方法,并为了调用时方便,也重载了这个方法,方法的具体代码及注释如下:/ / 获得函数指针/ / 调用函数的名称 public void loadfun(string lpprocname) / 若函数库模块的句柄为空,则抛出异常if(hmodule=intptr.zero) throw(new exception( 函数库模块的句柄为空, 请确保已进行loaddll 操作 !); / 取得函数指针far

16、proc = getprocaddress(hmodule,lpprocname); / 若函数指针,则抛出异常if(farproc=intptr.zero) throw(new exception( 没有找到:+lpprocname+ 这个函数的入口点); / / 获得函数指针/ / 包含需调用函数的dll 文件名 / 调用函数的名称 public void loadfun(string lpfilename,string lpprocname) / 取得函数库模块的句柄hmodule=loadlibrary(lpfilename); / 若函数库模块的句柄为空,则抛出异常if(hmodul

17、e=intptr.zero) throw(new exception( 没有找到:+lpfilename+. ); / 取得函数指针farproc = getprocaddress(hmodule,lpprocname); / 若函数指针,则抛出异常if(farproc=intptr.zero) throw(new exception( 没有找到:+lpprocname+ 这个函数的入口点); 6. 添加 unloaddll 及 invoke 方法, invoke 方法也进行了重载:/ / 卸载 dll / public void unloaddll() freelibrary(hmodule

18、); hmodule=intptr.zero; farproc=intptr.zero; invoke / / 调用所设定的函数/ / 实参 / 实参类型 / 实参传送方式 / 返回类型 / 返回所调用函数的object public object invoke(object objarray_parameter,type typearray_parametertype,modepass modepassarray_parameter,type type_return) / 下面3 个 if 是进行安全检查, 若不能通过, 则抛出异常if(hmodule=intptr.zero) throw(

19、new exception( 函数库模块的句柄为空, 请确保已进行loaddll 操作 !); if(farproc=intptr.zero) throw(new exception( 函数指针为空, 请确保已进行loadfun 操作 ! ) ); if(objarray_parameter.length!=modepassarray_parameter.length) throw(new exception( 参数个数及其传递方式的个数不匹配. ) ); / 下面是创建myassemblyname 对象并设置其name 属性assemblyname myassemblyname = new

20、assemblyname(); myassemblyname.name = invokefun; / 生成单模块配件assemblybuilder myassemblybuilder =appdomain.currentdomain.definedynamicassembly(myassemblyname,assemblybuilderaccess.run); modulebuilder mymodulebuilder =myassemblybuilder.definedynamicmodule(invokedll); / 定 义 要 调 用 的 方 法, 方 法 名 为 “ myfun ”,

21、 返 回 类 型 是 “ type_return ”参 数 类 型 是“ typearray_parametertype ” methodbuilder mymethodbuilder =mymodulebuilder.defineglobalmethod(myfun,methodattributes.public| methodattributes.static,type_return,typearray_parametertype); / 获取一个ilgenerator ,用于发送所需的il ilgenerator il = mymethodbuilder.getilgenerator()

22、; int i; for (i = 0; i objarray_parameter.length; i+) / 用循环将参数依次压入堆栈switch (modepassarray_parameter) case modepass.byvalue: il.emit(opcodes.ldarg, i); break; case modepass.byref: il.emit(opcodes.ldarga, i); break; default: throw(new exception( 第 +(i+1).tostring() + 个参数没有给定正确的传递方式. ) ); if (intptr.si

23、ze = 4) / 判断处理器类型il.emit(opcodes.ldc_i4, farproc.toint32(); else if (intptr.size = 8) il.emit(opcodes.ldc_i8, farproc.toint64(); else throw new platformnotsupportedexception(); il.emitcalli(opcodes.calli,callingconvention.stdcall,type_return,typearray_parametertype); il.emit(opcodes.ret); / 返回值mymod

24、ulebuilder.createglobalfunctions(); / 取得方法信息methodinfo mymethodinfo = mymodulebuilder.getmethod(myfun); return mymethodinfo.invoke(null, objarray_parameter);/ 调用方法,并返回其值 invoke / / 调用所设定的函数/ / 函数指针 / 实参 / 实参类型 / 实参传送方式 / 返回类型 / 返回所调用函数的object public object invoke(intptr intptr_function,object objarr

25、ay_parameter,type typearray_parametertype,modepass modepassarray_parameter,type type_return) / 下面2 个 if 是进行安全检查, 若不能通过, 则抛出异常if(hmodule=intptr.zero) throw(new exception( 函数库模块的句柄为空, 请确保已进行loaddll 操作 !); if(intptr_function=intptr.zero) throw(new exception( 函数指针intptr_function 为空! ) ); farproc=intptr_

26、function; return invoke(objarray_parameter,typearray_parametertype,modepassarray_parameter,type_return); 2) dld ” ,向 “form1窗体中添加和text “b3、“ 用 loadlibrary”,“b4 、“ 调用 count ”,并调整到适当的大小及位置。2 cs,在 “b3_click类实例:/ / 创建一个dld 类对象/ private dld myfun=new dld(); 3 在“b3_click myfun.loaddll(count.dll); / 加载count

27、.dll myfun.loadfun(_count4); / 调入函数count, _count4 是它的入口,可通过depends 查看4 cs,在 “b4_click object parameters = new object(int)0; / 实参为0 type parametertypes = new typetypeof(int); / 实参类型为int modepass themode=new modepassmodepass.byvalue; / 传送方式为值传type type_return = typeof(int); / 返回类型为int / 弹出提示框,显示调用myfu

28、n.invoke 方法的结果,即调用count 函数messagebox.show( 这是您装载该dll 后第+myfun.invoke(parameters,parametertypes,themode,type_return).tostring() + 次点击此按钮。, 挑战杯); 5 cs,在 “b5_click myfun.unloaddll(); 6 ” 运行该程序,并先点击按钮b3 加载 “count.dll”三次以调用3” ,先后弹出的提示框如下:经初始化后,再传入实参“0 也不会改变其值为“0 。7 以卸载 “count.dll进行装载 “count.dll查看调用了 “cou

29、nt(0) (三) 调用托管 dll 一般方法c# 是很简单的, 只要在 “ 解决方案资源管理器” 中的需要调用dll 或通过浏览来选择dll 导入相关的命名空间。(四) 动态调用托管dll c# 也需要借助system.reflection.assembly 。现在,用例子说明:,新建一个 visual c# ”,并在类 “class1 中及方法 count / 由于static 不能修饰方法体内的变量,所以需放在这里,且初始化值为int.minvalue static int s=int.minvalue; public int count(int init) / 判断 s 是否等于int

30、.minvalue ,是的话把init 赋值给s if(s=int.minvalue) s=init; s+; /s 自增1 return s; / 返回 s ” ,向“form1 窗体中添加属性为“b6 ,text 类来动态调用托管dll ,转入代码视图, 先导入命名空间: using system.reflection; 方法和 b6_click private object invoke(string lpfilename,string namespace,string classname,string lpprocname,object objarray_parameter) try

31、/ 载入程序集assembly myassembly=assembly.loadfrom(lpfilename); type type=myassembly.gettypes(); foreach(type t in type) / 查找要调用的命名空间及类if(t.namespace=namespace&t.name=classname) / 查找要调用的方法并进行调用methodinfo m=t.getmethod(lpprocname); if(m!=null) object o=activator.createinstance(t); return m.invoke(o,obj

32、array_parameter); else messagebox.show( 装载出错!); /try catch(system.nullreferenceexception e) messagebox.show(e.message); /catch return (object)0; / invoke ” 方法体内代码如下:/ 显示count(0) 返回的值messagebox.show( 这是您第+invoke(cscount.dll,cscount,class1,count,new object(int)0).tostring()+ 次点击此按钮。, 挑战杯); ” 的 bindebu

33、g 复制到项目 “tzb 文件夹中,按 “f5 运行该程序,并点击按钮b6 个提示框,内容分别是“ 这是您第 1 次点击此按钮。 ” 、“ 这是您第3 在这里的作用。(五) c#程序嵌入dll 的调用 dll 程序 byte”返回,然后就用 “assembly load(byte);”得到 dll 程序集, 最后就可以像上面的invoke中的方法进行调用。 当然不用上面方法也可以,如用接口实现动态调用, 但 dll 。 现在我只对像上面的invoke中的方法进行调用进行讨论,为了以后使用方便及实现代码的复用,我们可以结合上一个编写一个类。1) ldfs ”中新建一个名为ldfs ”,请注意,在

34、这个类中“resource 程序中的资源,它也可以是硬盘上任意一个 dll 的类中的方法loaddll ,如果找不到,就查找硬盘上的。using system.io; / 对文件的读写需要用到此命名空间using system.reflection; / 使用assembly 类需用此命名空间using system.reflection.emit; / 使用 ilgenerator 需用此命名空间:/ 记录要导入的程序集static assembly myassembly; 方法:private byte loaddll(string lpfilename) assembly nowasse

35、mbly = assembly.getentryassembly(); stream fs=null; try / 尝试读取资源中的dll fs = nowassembly.getmanifestresourcestream(nowassembly.getname().name+.+lpfilename); finally / 如果资源没有所需的dll ,就查看硬盘上有没有,有的话就读取if (fs=null&!file.exists(lpfilename) throw(new exception( 找不到文件:+lpfilename); else if(fs=null&fil

36、e.exists(lpfilename) filestream fs = new filestream(lpfilename, filemode.open); fs=(stream)fs; byte buffer = new byte(int) fs.length; fs.read(buffer, 0, buffer.length); fs.close(); return buffer; / 以 byte 返回读到的dll 方法来卸载 dll public void unloaddll() / 使 myassembly 指空myassembly=null; 方法来进行对dll cs 相同,不过这里用的是assembly.load” ,而且用了来保存已加载的dll 就进行加载,其代码如下所示:public object invoke(string lpfilename,string namespace,string classname,string lpprocname,object

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论