gis
gis
管理员
管理员
  • 注册日期2003-07-16
  • 发帖数15951
  • QQ
  • 铜币25345枚
  • 威望15368点
  • 贡献值0点
  • 银元0个
  • GIS帝国居民
  • 帝国沙发管家
  • GIS帝国明星
  • GIS帝国铁杆
阅读:1500回复:1

Delphi 中动态链接库(DLL)的建立和使用

楼主#
更多 发布于:2004-07-05 19:33
<P><FONT face=宋体>动态链接库是一个能够被应用程序和其它的</FONT>DLL<FONT face=宋体>调用的过程和函数的集合体,它里面包含的是公共代码或资源。由于</FONT>DLL<FONT face=宋体>代码使用了内存共享技术,在某些地方</FONT>windows<FONT face=宋体>也给了</FONT>DLL<FONT face=宋体>一些更高的权限,因而</FONT>DLL<FONT face=宋体>中可以实现一些一般程序所不能实现的功能,如实现</FONT>windows<FONT face=宋体>的</FONT>HOOK<FONT face=宋体>、</FONT>ISAPI<FONT face=宋体>等。同时,</FONT>DLL<FONT face=宋体>还为不同语言间代码共享提供了一条方便的途径。因而</FONT>DLL<FONT face=宋体>在编程时应用较为广泛,本文将介绍如何在</FONT> Delphi <FONT face=宋体>中建立和使用</FONT>DLL<FONT face=宋体>。</FONT></P>
<P><FONT face=宋体>一.</FONT>DLL <FONT face=宋体>库内存共享机制</FONT></P>
<P><FONT face=宋体>从使用效果看,</FONT>DLL<FONT face=宋体>和</FONT>unit <FONT face=宋体>很像,它们都可以被别的工程模块所调用,但二者在内部的实现机制上确存在着差别。如果一个程序模块中用</FONT>uses<FONT face=宋体>语句引用了某个</FONT>unit<FONT face=宋体>,编译程序在编译该模块时,便会连同</FONT>unit<FONT face=宋体>一起编译,并把编译后的可执行代码链接到本程序模块中,这就是一个程序模块能够调用所引用</FONT>unit<FONT face=宋体>中过程和函数的原因。当同一个</FONT>unit<FONT face=宋体>被多个工程所引用时,则每个工程中都含有该</FONT>unit<FONT face=宋体>的可执行代码,当含有该</FONT>unit<FONT face=宋体>的多个工程同时执行时,</FONT>unit<FONT face=宋体>的可执行代码会随不同工程而多次被调入内存,造成内存资源的浪费。</FONT>DLL<FONT face=宋体>则不同,它即使被某个工程调用,编译后仍是独立的,也就是说编译后,一个</FONT>DLL<FONT face=宋体>库形成一个单独的可执行文件,而不与任何其它的可执行文件连接在一起,因而</FONT>DLL<FONT face=宋体>库并不从属于某个特定的工程,当多个工程调用同一个</FONT>DLL<FONT face=宋体>库时只有第一个工程把</FONT>DLL<FONT face=宋体>库调入内存,其余工程并不重复调入同一个</FONT>DLL<FONT face=宋体>库到内存,而是到同一个共享内存区读取。并且,</FONT>DLL<FONT face=宋体>的执行代码是在程序运行期间动态调入的,而不是如</FONT>unit<FONT face=宋体>在程序运行时就与整个工程一起调入内存。这样便可消除</FONT>unit<FONT face=宋体>带来的相同代码多处占用内存的弊病。</FONT></P>
<P><FONT face=宋体>二</FONT> Delphi<FONT face=宋体>中</FONT>DLL<FONT face=宋体>库的建立</P>
<P> 在</FONT><FONT face="宋体, MS Song">Delphi</FONT><FONT face=宋体>环境中,编写一个</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>同编写一个一般的应用程序并没有太大的区别。事实上作为</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>主体的</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>函数的编写,除了在内存、资源的管理上有所不同外,并不需要其它特别的手段。    一般工程文件的格式为:</P>
<P>  </FONT><FONT face="宋体, MS Song">program</FONT><FONT face=宋体>  </FONT><FONT face="宋体, MS Song"> </FONT><FONT face=宋体>工程标题</FONT><FONT face="宋体, MS Song">;</FONT></P>
<P><FONT face=宋体> </P>
<P>  </FONT><FONT face="宋体, MS Song">uses</FONT><FONT face=宋体>    </FONT><FONT face="宋体, MS Song"> </FONT><FONT face=宋体>子句</FONT><FONT face="宋体, MS Song">;</FONT></P>
<P><FONT face=宋体> </P>
<P>  程序体</P>
<P>  而</FONT><FONT face="宋体, MS Song">DLLs</FONT><FONT face=宋体>工程文件的格式为:</P>
<P>  </FONT><FONT face="宋体, MS Song">library </FONT><FONT face=宋体>工程标题</FONT><FONT face="宋体, MS Song">;</FONT></P>
<P><FONT face=宋体> </P>
<P>  </FONT><FONT face="宋体, MS Song">uses </FONT><FONT face=宋体>子句</FONT><FONT face="宋体, MS Song">;</FONT></P>
<P><FONT face=宋体> </P>
<P>  </FONT><FONT face="宋体, MS Song">exprots </FONT><FONT face=宋体>子句</FONT><FONT face="宋体, MS Song">;</FONT></P>
<P><FONT face=宋体> </P>
<P>  程序体</P>
<P>  它们主要的区别有两点:</P>
<P>  </FONT><FONT face="宋体, MS Song">1.</FONT><FONT face=宋体>一般工程文件的头标用</FONT><FONT face="宋体, MS Song">program</FONT><FONT face=宋体>关键字,而</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>工程文件头标用</FONT><FONT face="宋体, MS Song">library </FONT><FONT face=宋体>关键字。不同的关键字通知编译器生成不同的可执行文件。用</FONT><FONT face="宋体, MS Song">program</FONT><FONT face=宋体>关键字生成的是</FONT><FONT face="宋体, MS Song">.exe</FONT><FONT face=宋体>文件,而用</FONT><FONT face="宋体, MS Song">library</FONT><FONT face=宋体>关键字生成的是</FONT><FONT face="宋体, MS Song">.dll</FONT><FONT face=宋体>文件;</P>
<P>  </FONT><FONT face="宋体, MS Song">2.</FONT><FONT face=宋体>假如</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>要输出供其它应用程序使用的函数或过程,则必须将这些函数或过程列在</FONT><FONT face="宋体, MS Song">exports</FONT><FONT face=宋体>子句中。而这些函数或过程本身必须用</FONT><FONT face="宋体, MS Song">export</FONT><FONT face=宋体>编译指令进行编译。</FONT></P>
<P><FONT face=宋体>在</FONT>Delphi<FONT face=宋体>主菜单</FONT>file <FONT face=宋体>中选</FONT>new...<FONT face=宋体>项,在弹出的窗口中双击</FONT>DLL<FONT face=宋体>图标,便会自动给出</FONT>DLL<FONT face=宋体>源模块框架</FONT>,<FONT face=宋体>如下:</FONT></P>
<P>Library project1;</P>
<P>{...<FONT face=宋体>注释</FONT>...}</P>
<P>uses</P>
<P>SysUtils, Classes;</P>
<P>begin</P>
<P>end.</P>
<P><FONT face=宋体> </P>
<P>接下来便可在</FONT><FONT face="宋体, MS Song">USES</FONT><FONT face=宋体>和</FONT><FONT face="宋体, MS Song">begin</FONT><FONT face=宋体>之间加入想在该</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>中实现的过程和函数的定义,并用</FONT><FONT face="宋体, MS Song">export</FONT><FONT face=宋体>和</FONT><FONT face="宋体, MS Song">exprots</FONT><FONT face=宋体>保字把它们引出,以便别的模块引用,在</FONT><FONT face="宋体, MS Song">begin</FONT><FONT face=宋体>和</FONT><FONT face="宋体, MS Song">end</FONT><FONT face=宋体>之间加入初始化代码,初始化代码是用来对</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>变量初始化的。应注意,即便无初始化代码</FONT><FONT face="宋体, MS Song">begin</FONT><FONT face=宋体>与</FONT><FONT face="宋体, MS Song">end</FONT><FONT face=宋体>也不可省略,如下例</FONT><FONT face="宋体, MS Song">:</FONT></P>
<P>library minmax;</P>
<P>function Min(X, Y: Integer): Integer; <FONT face=宋体 size=2>export;</FONT></P>
<P>begin</P>
<P>if X < Y then Min := X else Min := Y;</P>
<P>end;</P>
<P>function Max(X, Y: Integer): Integer; <FONT face=宋体 size=2>export;</FONT></P>
<P>begin</P>
<P>if X > Y then Max := X else Max := Y;</P>
<P>end;</P>
<P>exports</P>
<P>Min index 1,</P>
<P>Max index 2;</P>
<P>begin</P>
<P>end.</P>
<P><FONT face=宋体> </P>
<P>经编译后,并以</FONT><FONT face="宋体, MS Song">minmax.DLL</FONT><FONT face=宋体>存盘后,一个</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>库文件便形成了。</FONT></P>
<P><FONT face=宋体>三</FONT> DLL<FONT face=宋体>库的访问</P>
<P>访问</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>库有两种方式,一种是静态引用,另一种是动态引用。</P>
<P>用静态引用这种方法装入</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>要做两件事情:为</FONT><FONT face="宋体, MS Song">DLL </FONT><FONT face=宋体>库创建一个输入单元,以及用</FONT><FONT face="宋体, MS Song">USES</FONT><FONT face=宋体>把输入单元连接到要使用</FONT><FONT face="宋体, MS Song">DLL </FONT><FONT face=宋体>函数的程序模块中。为</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>库创建的输入单元与普通的单元的区别仅在于:在它的接口处声明的过程、函数,并不在它的实现部分给出真正的实现代码,而是用</FONT><FONT face="宋体, MS Song">external</FONT><FONT face=宋体>关键字把过程、函数的实现细节委托给外部</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>模块。</FONT></P>
<P>external<FONT face=宋体>命令的使用语法如下:</FONT></P>
<P>procedure /function <FONT face=宋体>过程</FONT>/<FONT face=宋体>函数名;</FONT>external DLL<FONT face=宋体>模块名;</P>
<P>下面给出为上面创建的</FONT><FONT face="宋体, MS Song">minmax.DLL</FONT><FONT face=宋体>库写的输入单元源文件</FONT><FONT face="宋体, MS Song">testdll .pas</FONT><FONT face=宋体>,从中可看出输入单元</FONT><FONT face="宋体, MS Song"> </FONT><FONT face=宋体>与一般</FONT><FONT face="宋体, MS Song"> </FONT><FONT face=宋体>单元的一些差别,代码如下所示:</FONT></P>
<P>unit testdll;</P>
<P>interface</P>
<P>uses</P>
<P>function Min (X, Y: Integer): Integer;</P>
<P>function Max (X, Y: Integer): Integer;</P>
<P> </P>
<P>implementation</P>
<P> </P>
<P>function Min; external ‘minmax.DLL’;</P>
<P>function Max; external ‘minmax.DLL’;</P>
<P>end.</P>
<P><FONT face=宋体> </P>
<P>一个应用程序若想调用</FONT><FONT face="宋体, MS Song">minmax.DLL</FONT><FONT face=宋体>中的函数,只须在其</FONT><FONT face="宋体, MS Song">uses</FONT><FONT face=宋体>语句中加入</FONT><FONT face="宋体, MS Song">testdll </FONT><FONT face=宋体>单元即可。</P>
<P> </FONT><FONT face="宋体, MS Song"> </FONT><FONT face=宋体>动态装入</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>,要用到</FONT><FONT face="宋体, MS Song">Windows</FONT><FONT face=宋体>的三个</FONT><FONT face="宋体, MS Song">API</FONT><FONT face=宋体>函数。</FONT><FONT face="宋体, MS Song">Loadlibrary</FONT><FONT face=宋体>、</FONT><FONT face="宋体, MS Song">Freelibrary</FONT><FONT face=宋体>和</FONT><FONT face="宋体, MS Song">GetprocAddress </FONT><FONT face=宋体>。</FONT><FONT face="宋体, MS Song">loadlibrary</FONT><FONT face=宋体>函数用来装入</FONT><FONT face="宋体, MS Song">DLL</FONT><FONT face=宋体>库,其调用格式如下:</FONT></P>
<P>function loadlobrary (DLLfileName<FONT face=宋体>:</FONT>Pchar): THandle:</P>
<P><FONT face=宋体>当不再需要一个</FONT>DLL<FONT face=宋体>库时,应调用</FONT>FreeLibrary<FONT face=宋体>函数将其释放,以空出宝贵的内存资源,其调用格式如下:</FONT></P>
<P>procedure FreeLibrary (Libmodule:THandle)</P>
<P>Libmodule <FONT face=宋体>为由</FONT>LoadLibrary<FONT face=宋体>调用得到的</FONT>DLL<FONT face=宋体>库句柄。在用</FONT>loadlobrary <FONT face=宋体>函数装入某个</FONT>DLL<FONT face=宋体>库和调用</FONT>FreeLibrary<FONT face=宋体>释放该</FONT>DLL<FONT face=宋体>库之间的程序段中,</FONT> <FONT face=宋体>可以使用该</FONT>DLL<FONT face=宋体>库中的过程和函数,具体使用方法是:用</FONT>GetprocAddress<FONT face=宋体>函数把</FONT>DLL<FONT face=宋体>库中函数的地址传递给程序中某个函数变量,再用该变量实现</FONT>DLL<FONT face=宋体>函数的调用。</FONT>GetprocAddress<FONT face=宋体>函数声名如下,</FONT></P>
<P>function GetprocAddress (Libmodule:THandle:procname:pchar):TFarProc:</P>
<P><FONT face=宋体> </P>
<P>如下例所示:</FONT></P>
<P>type</P>
<P>TTimeRec = record</P>
<P>Second: Integer;</P>
<P>Minute: Integer;</P>
<P>Hour: Integer;</P>
<P>end;</P>
<P>TGetTime = procedure(var Time: TTimeRec);</P>
<P>THandle = Integer;</P>
<P>var</P>
<P>Time: TTimeRec;</P>
<P>Handle: THandle;</P>
<P>GetTime: TGetTime;</P>
<P>...</P>
<P>begin</P>
<P>Handle := LoadLibrary('DATETIME.DLL');</P>
<P>if Handle <> 0 then</P>
<P>begin</P>
<P>@GetTime := GetProcAddress(Handle, 'GetTime');</P>
<P>if @GetTime <> nil then</P>
<P>begin</P>
<P>GetTime(Time);</P>
<P>with Time do</P>
<P>WriteLn('The time is ', Hour, ':', Minute, ':', Second);</P>
<P>end;</P>
<P>FreeLibrary(Handle);</P>
<P>end;</P>
<P>end;</P>
<P><FONT face=宋体>在调用动态链接库时应注意,</FONT> <FONT face=宋体>所需动态链接库须与应用程序在同一目录或</FONT>Windows System <FONT face=宋体>目录下。</FONT></P>
<P><FONT face=宋体>动态链接库是</FONT> Windows<FONT face=宋体>下程序组织的一种重要方式,使用动态链接库可以极大地保护用户在不同开发工具、不同时期所做的工作,提高编程效率。</P></FONT>
喜欢0 评分0
GIS麦田守望者,期待与您交流。
lgsunny
路人甲
路人甲
  • 注册日期2004-06-30
  • 发帖数68
  • QQ
  • 铜币221枚
  • 威望0点
  • 贡献值0点
  • 银元0个
1楼#
发布于:2004-07-13 16:33
<P>不错啊。</P><img src="images/post/smile/dvbbs/em01.gif" /><img src="images/post/smile/dvbbs/em02.gif" />
举报 回复(0) 喜欢(0)     评分
游客

返回顶部