📄 024.htm
字号:
<p> 一个</font>DLLs<font face="宋体" lang="ZH-CN">没有自己的堆栈段</font>(SS)<font
face="宋体" lang="ZH-CN">,它使用调用它的应用程序的堆栈。因此在</font>DLL<font
face="宋体" lang="ZH-CN">中的过程、函数绝对不要假定</font>DS = SS<font
face="宋体" lang="ZH-CN">。一些语言在小模式编译下有这种假设,但使用</font>Delphi<font
face="宋体" lang="ZH-CN">可以避免这种情况。</font>Delphi<font face="宋体"
lang="ZH-CN">绝不会产生假定</font>DS = SS<font face="宋体" lang="ZH-CN">的代码,</font>Delphi<font
face="宋体" lang="ZH-CN">的任何运行时间库过程</font>/<font face="宋体"
lang="ZH-CN">函数也都不作这种假定。需注意的是如果读者想嵌入汇编语言代码,绝不要使</font>SS<font
face="宋体" lang="ZH-CN">和</font>DS<font face="宋体" lang="ZH-CN">登录同一个值。 </p>
</font><p>10.2.1.4 DLLs<font face="宋体" lang="ZH-CN">中的运行时间错和处理 </p>
<p> 由于</font>DLLs<font face="宋体" lang="ZH-CN">无法控制应用程序的运行,导致很难进行异常处理,因此编写</font>DLLs<font
face="宋体" lang="ZH-CN">时要十分小心,以确保被调用时能正常执行</font>
<font face="宋体" lang="ZH-CN">。当</font>DLLs<font face="宋体" lang="ZH-CN">中发生一个运行时间错时,相应</font>DLLs<font
face="宋体" lang="ZH-CN">并不一定从内存中移去(因为此时其它应用程序可能正在用它),而调用</font>DLLs<font
face="宋体" lang="ZH-CN">的程序异常中止。这样造成的问题是当</font>DLLs<font
face="宋体" lang="ZH-CN">已被修改,重新进行调用时,内存中保留的仍然可能是以前的版本,修改后的程序并没有得到验证。对于这个问题,有以下两种解决方法:</p>
<p> </font>1.<font face="宋体" lang="ZH-CN">在程序的异常处理部分显式将</font>DLL<font
face="宋体" lang="ZH-CN">卸出内存;</p>
<p> </font>2.<font face="宋体" lang="ZH-CN">完全退出</font>Windows<font
face="宋体" lang="ZH-CN">,而后重新启动,运行相应的程序。</p>
<p> 同一般的应用程序相比,</font>DLL<font face="宋体" lang="ZH-CN">中运行时间错的处理是很困难的,而造成的后果也更为严重。因此要求程序设计者在编写代码时要有充分、周到的考虑。 </p>
</font><p>10.2.1.5 <font face="宋体" lang="ZH-CN">库初始化代码的编写 </p>
<p> 传统</font>Windows<font face="宋体" lang="ZH-CN">中动态链接库的编写,需要两个标准函数:</font>LibMain<font
face="宋体" lang="ZH-CN">和</font>WEP<font face="宋体" lang="ZH-CN">,用于启动和关闭</font>DLL<font
face="宋体" lang="ZH-CN">。在</font>LibMain<font face="宋体" lang="ZH-CN">中,可以执行开锁</font>DLL<font
face="宋体" lang="ZH-CN">数据段、分配内存、初始化变量等初始化工作;而</font>WEP<font
face="宋体" lang="ZH-CN">在从内存中移去</font>DLLs<font face="宋体"
lang="ZH-CN">前被调用,一般用于进行必要的清理工作,如释放内存等。</font>Delphi<font
face="宋体" lang="ZH-CN">用自己特有的方式实现了这两个标准函数的功能。这就是在工程文件中的</font>begin...end<font
face="宋体" lang="ZH-CN">部分添加初始化代码。和传统</font>Windows<font
face="宋体" lang="ZH-CN">编程方法相比,它的主要特色是:</p>
<p> </font>1.<font face="宋体" lang="ZH-CN">初始化代码是可选的。一些必要的工作(如开锁数据段)可以由系统自动完成。所以大部分情况下用户不会涉及到;</p>
<p> </font>2.<font face="宋体" lang="ZH-CN">可以设置多个退出过程,退出时按顺序依次被调用;</p>
<p> </font>3.LibMain<font face="宋体" lang="ZH-CN">和</font>WEP<font face="宋体"
lang="ZH-CN">对用户透明,由系统自动调用。</p>
<p> 初始化代码完成的主要工作是:</p>
<p> </font>1.<font face="宋体" lang="ZH-CN">初始化变量、分配全局内存块、登录窗口对象等初始化工作。在</font>(10.3.2)<font
face="宋体" lang="ZH-CN">节“利用</font>DLLs<font face="宋体" lang="ZH-CN">实现应用程序间的数据传输”中,用于数据共享的全局内存块就是在初始化代码中分配的。</p>
<p> </font>2.<font face="宋体" lang="ZH-CN">设置</font>DLLs<font face="宋体"
lang="ZH-CN">退出时的执行过程。</font>Delphi<font face="宋体" lang="ZH-CN">有一个预定义变量</font>ExitProc<font
face="宋体" lang="ZH-CN">用于指向退出过程的地址。用户可以把自己的过程名赋给</font>ExitProc<font
face="宋体" lang="ZH-CN">。系统自动调用</font>WEP<font face="宋体" lang="ZH-CN">函数,把</font>ExitProc<font
face="宋体" lang="ZH-CN">指向的地址依次赋给</font>WEP<font face="宋体"
lang="ZH-CN">执行,直到</font>ExitProc<font face="宋体" lang="ZH-CN">为</font>nil<font
face="宋体" lang="ZH-CN">。</p>
<p> 下边的一段程序包含一个退出过程和一段初始化代码,用来说明如何正确设置退出过程。 </p>
</font><p>library Test;</p>
<p>{$S-}</p>
<p>uses WinTypes, WinProcs;</p>
<p>var</p>
<p>SaveExit: Pointer; </p>
<p>procedure LibExit; far;</p>
<p>begin</p>
<p>if ExitCode = wep_System_Exit then</p>
<p>begin</p>
<p>{ <font face="宋体" lang="ZH-CN">系统关闭时的相应处理</font> } </p>
<p>end </p>
<p>else</p>
<p>begin</p>
<p>{ DLL<font face="宋体" lang="ZH-CN">卸出时的相应处理</font> }</p>
<p>end;</p>
<p>ExitProc := SaveExit; { <font face="宋体" lang="ZH-CN">恢复原来的退出过程指针</font>
}</p>
<p>end; </p>
<p>begin</p>
<p>{DLL<font face="宋体" lang="ZH-CN">的初始化工作</font> }</p>
<p>SaveExit := ExitProc; { <font face="宋体" lang="ZH-CN">保存原来的退出过程指针</font>
}</p>
<p>ExitProc := @LibExit; { <font face="宋体" lang="ZH-CN">安装新的退出过程</font>
}</p>
<p>end.</p>
<font face="宋体" lang="ZH-CN"><p> 在初始化代码中,首先把原来的退出过程指针保存到一个变量中,而后再把新的退出过程地址赋给</font>ExitProc<font
face="宋体" lang="ZH-CN">。而在自定义退出过程</font>LibExit<font face="宋体"
lang="ZH-CN">结束时再把</font>ExitProc<font face="宋体" lang="ZH-CN">的值恢复。由于</font>ExitProc<font
face="宋体" lang="ZH-CN">是一个系统全局变量,所以在结束时恢复原来的退出过程是必要的。</p>
<p> 退出过程</font>LibExit<font face="宋体" lang="ZH-CN">中使用了一个系统定义变量</font>ExitCode<font
face="宋体" lang="ZH-CN">,用于标志退出时的状态。</font> ExitCode<font
face="宋体" lang="ZH-CN">的取值与意义如下: </p>
</font><p><font face="宋体" lang="ZH-CN">表</font>10.1 ExitCode<font face="宋体"
lang="ZH-CN">的取值与意义</font></p>
<p><font face="宋体" lang="ZH-CN">━━━━━━━━━━━━━━━━━━━━━</p>
<p>取 值 意 义</font></p>
<p><font face="宋体" lang="ZH-CN">—————————————————————</p>
<p> </font>WEP_System_Exit Windows<font face="宋体" lang="ZH-CN">关闭 </p>
</font><p>WEP_Free_DLLx DLLs<font face="宋体" lang="ZH-CN">被卸出</font></p>
<p><font face="宋体" lang="ZH-CN">━━━━━━━━━━━━━━━━━━━━━ </p>
<p> 退出过程编译时必须关闭</font>stack_checking<font face="宋体"
lang="ZH-CN">,因而需设置编译指示</font> {$S-} <font face="宋体" lang="ZH-CN">。 </p>
</font><p>10.2.1.6 <font face="宋体" lang="ZH-CN">编写一般</font>DLLs<font
face="宋体" lang="ZH-CN">的应用举例 </p>
<p> 在下面的程序中我们把一个字符串操作的函数储存到一个</font>DLLs<font
face="宋体" lang="ZH-CN">中,以便需要的时候调用它。应该注意的一点是:为了保证这个函数可以被其它语言编写的程序所调用,作为参数传递的字符串应该是无结束符的字符数组类型</font>(<font
face="宋体" lang="ZH-CN">即</font>PChar<font face="宋体" lang="ZH-CN">类型</font>)<font
face="宋体" lang="ZH-CN">,而不是</font>Object Pascal<font face="宋体"
lang="ZH-CN">的带结束符的</font>Srting<font face="宋体" lang="ZH-CN">类型。程序清单如下:</p>
</font><p>library Example;</p>
<p>uses</p>
<p>SysUtils,</p>
<p>Classes;</p>
<p>{<font face="宋体" lang="ZH-CN">返回字符在字符串中的位置}</p>
</font><p>function InStr(SourceStr: PChar;Ch: Char): Integer; export;</p>
<p>var</p>
<p>Len,i: Integer;</p>
<p>begin</p>
<p>Len := strlen(SourceStr);</p>
<p>for i := 0 to Len-1 do</p>
<p>if SourceStr[i] = ch then</p>
<p>begin</p>
<p>Result := i;</p>
<p>Exit;</p>
<p>end;</p>
<p>Result := -1;</p>
<p>end;</p>
<p>exports</p>
<p>Instr Index 1 name 'MyInStr' resident;</p>
<p>begin</p>
<p>end. </p>
<p>10.2.2 <font face="宋体" lang="ZH-CN">调用</font>DLLs</p>
<font face="宋体" lang="ZH-CN"><p> 有两种方法可用于调用一个储存在</font>DLLs<font
face="宋体" lang="ZH-CN">中的过程。</p>
<p> </font>1.<font face="宋体" lang="ZH-CN">静态调用或显示装载</p>
<p> 使用一个外部声明子句,使</font>DLLs<font face="宋体" lang="ZH-CN">在应用程序开始执行前即被装入。例如: </p>
<p> </font>function Instr(SourceStr : PChar;Check : Char); Integer; far; external
'UseStr';</p>
<font face="宋体" lang="ZH-CN"><p> 使用这种方法,程序无法在运行时间里决定</font>DLLs<font
face="宋体" lang="ZH-CN">的调用。假如一个特定的</font>DLLs<font face="宋体"
lang="ZH-CN">在运行时无法使用,则应用程序将无法执行。</p>
<p> </font>2.<font face="宋体" lang="ZH-CN">动态调用或隐式装载</p>
<p> 使用</font>Windows API<font face="宋体" lang="ZH-CN">函数</font>LoadLibray<font
face="宋体" lang="ZH-CN">和</font>GetProcAddress<font face="宋体" lang="ZH-CN">可以实现在运行时间里动态装载</font>DLLs<font
face="宋体" lang="ZH-CN">并调用其中的过程。</p>
<p> 若程序只在其中的一部分调用</font>DLLs<font face="宋体"
lang="ZH-CN">的过程,或者程序使用哪个</font>DLLs<font face="宋体"
lang="ZH-CN">,</font> <font face="宋体" lang="ZH-CN">调用其中的哪个过程需要根据程序运行的实际状态来判断,那么使用动态调用就是一个很好的选择。</p>
<p> 使用动态调用,即使装载一个</font>DLLs<font face="宋体"
lang="ZH-CN">失败了,程序仍能继续运行。 </p>
</font><p>10.2.3 <font face="宋体" lang="ZH-CN">静态调用</p>
<p> 在静态调用一个</font>DLLs<font face="宋体" lang="ZH-CN">中的过程或函数时,</font>external<font
face="宋体" lang="ZH-CN">指示增加到过程或函数的声明语句中。被调用的过程或函数必须采用远调用模式。这可以使用</font>far<font
face="宋体" lang="ZH-CN">过程指示或一个{</font>$F +}<font face="宋体"
lang="ZH-CN">编译指示。</p>
<p> </font>Delphi<font face="宋体" lang="ZH-CN">全部支持传统</font>Windows<font
face="宋体" lang="ZH-CN">动态链接库编程中的三种调用方式,它们是:</p>
<p> ●</font> <font face="宋体" lang="ZH-CN">通过过程</font>/<font
face="宋体" lang="ZH-CN">函数名</p>
<p> ●</font> <font face="宋体" lang="ZH-CN">通过过程</font>/<font
face="宋体" lang="ZH-CN">函数的别名</p>
<p> ●</font> <font face="宋体" lang="ZH-CN">通过过程</font>/<font
face="宋体" lang="ZH-CN">函数的顺序号 </p>
<p> 通过过程或函数的别名调用,给用户编程提供了灵活性,而通过顺序号</font>(Index)<font
face="宋体" lang="ZH-CN">调用可以提高相应</font>DLL<font face="宋体"
lang="ZH-CN">的装载速度。 </p>
</font><p>10.2.4 <font face="宋体" lang="ZH-CN">动态调用 </p>
</font><p>10.2.4.1 <font face="宋体" lang="ZH-CN">动态调用中的</font>API<font
face="宋体" lang="ZH-CN">函数 </p>
<p> 动态调用中使用的</font>Windows API<font face="宋体" lang="ZH-CN">函数主要有三个</font>,<font
face="宋体" lang="ZH-CN">即:</font>Loadlibrary<font face="宋体" lang="ZH-CN">,</font>GetProcAddress<font
face="宋体" lang="ZH-CN">和</font>Freelibrary<font face="宋体" lang="ZH-CN">。</p>
<p> </font> 1.Loadlibrary: <font face="宋体" lang="ZH-CN">把指定库模块装入内存</p>
<p> 语法为: </p>
<p> </font>function Loadlibrary(LibFileName: PChar): THandle; </p>
<p>LibFileName<font face="宋体" lang="ZH-CN">指定了要装载</font>DLLs<font
face="宋体" lang="ZH-CN">的文件名,如果</font>LibFileName<font face="宋体"
lang="ZH-CN">没有包含一个路径,则</font>Windows<font face="宋体" lang="ZH-CN">按下述顺序进行查找:</p>
<p> </font>(1)<font face="宋体" lang="ZH-CN">当前目录;</p>
<p> </font>(2)Windows<font face="宋体" lang="ZH-CN">目录</font>(<font
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -