Delphi下的DLL编程Word文件下载.docx

上传人:b****3 文档编号:17574155 上传时间:2022-12-07 格式:DOCX 页数:12 大小:27.57KB
下载 相关 举报
Delphi下的DLL编程Word文件下载.docx_第1页
第1页 / 共12页
Delphi下的DLL编程Word文件下载.docx_第2页
第2页 / 共12页
Delphi下的DLL编程Word文件下载.docx_第3页
第3页 / 共12页
Delphi下的DLL编程Word文件下载.docx_第4页
第4页 / 共12页
Delphi下的DLL编程Word文件下载.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

Delphi下的DLL编程Word文件下载.docx

《Delphi下的DLL编程Word文件下载.docx》由会员分享,可在线阅读,更多相关《Delphi下的DLL编程Word文件下载.docx(12页珍藏版)》请在冰豆网上搜索。

Delphi下的DLL编程Word文件下载.docx

在进行后面的讲解之前,我想大家应该先清楚一个概念:

例程声明的是一个指针变量,调用函数/过程,其实是通过指针转入该函数/过程的执行代码。

我们先尝试用Delphi来建立一个自己的DLL文件。

这个DLL包含一个标准的目录删除(包含子目录及文件)函数。

建立DLL

通过Delphi建立一个DLL是很容易的。

New一个新Project,选择DLLWizard,然后会生成一个非常简单的单元。

该单元不象一般的工程文件以program开始,而是以library开始的。

该工程单元缺省引用了SysUtils、Classes两个单元。

可以直接在该单元的uses之后,begin…end部分之前添加函数/过程代码,也可以在工程中添加包含代码的单元,然后该单元将会被自动uses。

接下来是编写DLL例程的代码。

如果是引用单元里的例程,需要通过声明时添加export后缀引出。

假如是直接写在library单元中的,则不必再写export了。

最后一步是在library单元的begin语句之上,uses部分及函数定义之下添加exports部分,并列举需要引出的例程名称。

注意仅仅是名称,不包含procedure或function关键字,也不需要参数、返回值和后缀。

exports语句后的语法有三种形式(例程指具体的函数/过程):

exports例程名;

exports例程名 

index 

索引值;

name新名称;

索引值和新名称便于其他程序确定函数地址;

也可以不指定,如果没有使用Index关键字,Delphi将按照exports后的顺序从1开始自动分配索引号。

Exports后可跟多个例程,之间以逗号分隔。

编译,build最终的dll文件。

需注意的格式

为了保证生成的DLL能正确与C++等语言兼容,需要注意以下几点:

尽量使用简单类型或指针作为参数及返回值的类型。

这里的简单类型是指C++的简单类型,所以string字符串类型最好转换成Pchar字符指针。

直接使用string的DLL例程在Delphi开发的程序中调用是没有问题的(有资料指出需加入ShareMem做为第一单元以确保正确),但如果使用C++或其他语言开发的程序调用,则不能保证参数传递正确;

虽然过程是允许的,但是最好习惯全部写成函数。

过程则返回执行正确与否的true/false;

对于参数的指示字比如const(只读)、out(只写)等等,为保证调用的兼容性,最好使用缺省方式(缺省var,即可读写的地址);

使用stdcall声明后缀,以保证正确的异常处理。

16位DLL无法通过这种方式处理异常,所以还得在例程最外层用Try…Except将异常处理掉;

一般不使用far后缀,除非为了保持与16位兼容。

范例代码

DLL工程单元:

libraryFileOperate;

uses

 

SysUtils,

Classes,

uDirectoryin‘uDirectory.pas’;

{$R*.res}

exports

DeleteDir;

begin

end.

函数功能实现单元:

unituDirectory;

interface

Classes,SysUtils;

functionDeleteDir(DirName:

Pchar):

boolean;

export;

stdcall;

implementation

functionDeleteDir(DirName:

var

FindFile:

TSearchRec;

s:

string;

=DirName;

ifcopy(s,length(s),1)<

>

‘\’thens:

=s+‘\’;

ifDirectoryExists(s)thenbegin

ifFindFirst(s+‘*.*’,faAnyFile,FindFile)=0thenbegin

repeat

ifFindFile.Attr<

faDirectorythenbegin

//文件则删除

DeleteFile(s+FindFile.Name);

end

elsebegin

//目录则嵌套自身

if(FindFile.Name<

‘.’)and(FindFile.Name<

‘..’)then

DeleteDir(Pchar(s+FindFile.Name));

end;

untilFindNext(FindFile)<

0;

FindCLose(FindFile);

Result:

=RemoveDir(s);

end;

初始化及释放资源

Delphi中初始化有几种方法。

一种是利用Unit的Initalization与Finalization这两个小节(不知道“单元小节”?

你该先去恶补Delphi语法了)进行该单元中变量的初始化工作。

注意,DLL虽然在内存中只有一个副本,但是例程隶属于调用者的不同进程空间。

如果想初始化公共变量来达到多进程共享是不可行的,同时也要注意公共变量带来的冲突问题。

二是在library单元的begin…end部分进行DLL的初始化。

假如想在DLL结束时有对应代码,则可以利用DLL自动创建的一个ExitProc过程变量,这是一个退出过程的指针。

建立一个自己的过程,并将该过程的地址赋与ExitProc。

因为ExitProc是DLL创建时就存在的,所以在begin…end部分就应该进行此步操作。

同时建立一个临时指针变量保存最初的ExitProc值,在自己的退出过程中将ExitProc值赋回来。

这样做是为了进行自己的退出操作后,能完成缺省的DLL退出操作(与在重载的Destory方法中inherated的意义是一样的,完成了自己的destory,还需要进行缺省的父类destory才完整)。

示例如下:

library 

MyDLL;

OldExitProc:

pointer;

//公共变量,为的保存最初的ExitProc指针以便赋回

procedure 

MyExitProc;

//对应初始化的结束代码

ExitProc 

:

OldExitProc;

//自己的退出过程中要记住将ExitProc赋回

  

//初始化代码

OldExitProc 

ExitProc;

ExitProc 

@MyExitProc;

end.

第三种方法和ExitProc类似,在System单元中预定义了一个指针变量DllProc(该方法需要引用Windows单元)。

在使用DLLProc时, 

必须先写好一个具有以下原型的程序:

  procedure 

DLLHandler(dwReason:

DWORD);

stdcall;

并在library的begin..end.之间, 

将这个DLLHandler程序的执行地址赋给DLLProc中, 

这时就可以根据参数Reason的值分别作出相应的处理。

示例如下:

  library 

  …

MyDLLHandler(dwReason:

  begin

   case 

dwReasonof

    DLL_Process_Attach:

//进程进入时

    DLL_Process_Detach:

//进程退出时

    DLL_Thread_Attach:

//线程进入时

    DLL_Thread_Detach:

//线程退出时

   end;

  end;

   

… 

  DLLProc 

@MyDLLHandler;

 MyDLLHandle(DLL_Process_Attach);

  end.

可见,通过DLLProc 

在处理多进程时比ExitProc更加强大和灵活。

静态(隐式)调用DLL

DLL已经有了,接下来我们看如何调用并调试它。

普通的DLL是不需要注册的,但是要包含在windows搜索路径中才能被找到。

搜索路径的顺序是:

当前目录;

Path路径;

windows目录;

widows系统目录(system、system32)。

引入DLL例程的声明方法

在需要使用外部例程(DLL函数/过程)的代码之前预定义该函数/过程。

即按DLL中的定义原样声明,且仅需要声明。

同时加上external后缀引入,与export引出相对应。

根据exports的三种索引语法,也有三种确定例程的方式(以函数声明为例):

function函数名(参数表):

返回值;

external’DLL文件名’;

external’DLL文件名’index索引号;

external’DLL文件名’name新名称;

如果不确定例程名称,可以用索引方式引入。

如果按原名引入会发生冲突,则可以用“新名称”引入。

进行声明后DLL函数的使用就和一般函数相同了。

静态调用方式简单,但在启动调用程序时即调入DLL作为备用过程。

如果此DLL文件不存在,那么启动时即会提示错误并立刻终止程序,不管定义是否使用。

快速查看DLL例程定义可以使用Borland附带的工具tdump.exe(在Delphi或BCB的bin目录下),示例如下:

Tdumpc:

\windows\system\user32.dll>

user32.txt

然后打开user32.txt文件,找到ExportsfromUSER32.dll行,之下的部分就是DLL例程定义了,比如:

RVA 

Ord.HintName

——–—-—-—-

00001371 

10000ActivateKeyboardLayout

00005C20 

20001AdjustWindowRect

0000161B 

30002AdjustWindowRectEx

Name列就是例程的名称,Ord就是该例程索引号。

注意,该工具是不能得到例程的参数表的。

如果参数错误,调用DLL例程会引起堆栈错误而导致调用程序崩溃。

调用代码

建立一个普通工程,在Main窗体上放置一个TShellTreeView控件(Samples页),再放置一个按钮,添加代码如下:

external‘FileOperate.dll’;

procedureTForm1.Button1Click(Sender:

TObject);

ifDirectoryExists(ShellTreeView.Path)then

ifApplication.MessageBox(Pchar(‘确定删除目录’+QuotedStr(ShellTreeView.Path)+’吗?

’),‘Information’,MB_YESNO)=IDYesthen

ifDeleteDir(PChar(ShellTreeView.Path))then

showmessage(‘删除成功’);

该范例调用的就是前面建立的DLL。

注意,声明时要包括stdcall后缀,这样才能保证调用Delphi开发的DLL的例程中类似PChar这样的参数值传递正确。

大家有兴趣可以试验一下,不加入stdcall或者safecall后缀执行上面代码,将不能保证成功传递字符串参数给DLL函数。

调试方法

在Delphi主菜单Run项目中选择Parameters,打开“RunParameters”对话框。

在HostApplication中填入一个宿主程序(该程序调用了将要调试的DLL),还可以在Parameters中输入参数。

保存内容,然后就可以在DLL工程中设置断点、跟踪/单步执行了。

Run该DLL工程,然后将运行宿主程序。

执行会调用DLL的操作,然后就能跟踪进入该DLL的代码,接下来的调试操作和普通程序是一样的。

因为操作系统或其他软件影响的原因,可能会出现进行了上述步骤仍然无法正常跟踪/中断DLL代码的情况。

这时可以试试在菜单Project 

|Options 

对话框的 

Linker 

页面里将 

EXE 

andDLLOptions 

中的Include 

TD32 

debug 

info及include 

remote 

symbols两个选项选中。

假如还是不能中断-_____-|||那只好另外建立一个引用执行代码单元的应用程序,写代码调用例程调试完成后再编译DLL了(其实该方法有时候蛮方便的,但有时候亦非常麻烦)。

引入文件

DLL比较复杂时,可以为它的声明专门创建一个引入单元,这会使该DLL变得更加容易维护和查看。

引入单元的格式如下:

  unit 

MyDllImport;

{Import 

unit 

for 

MyDll.dll}

  interface

  procedure 

MyDllProc;

implementation

 procedure 

external 

’MyDll’ 

1;

这样以后想要使用MyDll中的例程时,只要简单的在程序模块中的uses子句中加上MyDllImport即可。

其实这仅仅是种方便开发的技巧,大家打开Windows等引入windowsAPI的单元,可以看到类似的做法。

动态(显式)调用DLL

前面讲述静态调用DLL时提到,DLL会在启动调用程序时即被调入。

所以这样的做法只能起到公用DLL以及减小运行文件大小的作用,而且DLL装载出错会立刻导致整个启动过程终止,哪怕该DLL在运行中只起到微不足道的作用。

使用动态调用DLL的方式,仅在调用外部例程时才将DLL装载内存(引用记数为0时自动将该DLL从内存中清除),从而节约了内存空间。

而且可以判断装载是否正确以避免调用程序崩溃的情况,最多损失该例程功能而已。

动态调用虽然有上述优点,但是对于频繁使用的例程,因DLL的调入和释放会有额外的性能损耗,所以这样的例程则适合使用静态引入。

调用范例

DLL动态调用的原理是首先声明一个函数/过程类型并创建一个指针变量。

为了保证该指针与外部例程指针一致以确保赋值正确,函数/过程的声明必须和外部例程的原始声明兼容(兼容的意思是1、参数名称可以不一样;

2、参数/返回值类型至少保持可以相互赋值,比如原始类型声明为Word,新的声明可以为Integer,假如传递的实参总是在Word的范围内,就不会出错)。

接下来通过windowsAPI函数LoadLibrary引入指定的库文件,LoadLibrary的参数是DLL文件名,返回一个THandle。

如果该步骤成功,再通过另一个API函数GetProcAddress获得例程的入口地址,参数分别为LoadLibrary的指针和例程名,最终返回例程的入口指针。

将该指针赋值给我们预先定义好的函数/过程指针,然后就可以使用这个函数/过程了。

记住最后还要使用API函数FreeLibrary来减少DLL引用记数,以保证DLL使用结束后可以清除出内存。

这三个API函数的Delphi声明如下:

FunctionLoadLibrary(LibFileName:

PChar):

THandle;

FunctionGetProcAddress(Module:

ProcName:

TfarProc;

ProcedureFreeLibrary(LibModule:

THandle);

将前面静态调用DLL例程的代码更改为动态调用,如下所示:

type

TDllProc=function(PathName:

LibHandle:

THandle;

DelPath 

:

TDllProc;

LibHandle:

=LoadLibrary(PChar(‘FileOperate.dll’));

ifLibHandle>

=32thenbegin

try

DelPath:

=GetProcAddress(LibHandle,PChar(‘DeleteDir’));

ifDelPath(PChar(ShellTreeView.Path))then

finally

FreeLibrary(LibHandle);

16位DLL的动态调入

下面将演示一个16位DLL例程调用的例子,该例程是windows9x中的一个隐藏API函数。

代码混合了静态、动态调用两种方式,除了进一步熟悉外,还可以看到调用16位DLL的解决方法。

先解释一下问题所在:

我要实现的功能是获得win9x的“系统资源”。

在winNT/2000下是没有“系统资源”这个概念的,因为winNT/2000中堆栈和句柄不再象win9X那样被限制在64K大小。

为了取该值,可以使用win9x的userdll中一个隐藏的API函数GetFreeSystemResources。

该DLL例程必须动态引入。

如果静态声明的话,在win2000里执行就会立即出错。

这个兼容性不解决是不行的。

所以必须先判断系统版本,如果是win9x再动态加载。

检查操作系统版本的代码是:

OSversion 

_OSVERSIONINFOA;

FWinVerIs9x:

Boolean;

OSversion.dwOSVersionInfoSize :

= sizeof(_OSVERSIONINFOA);

GetVersionEx(OSversion);

FWinVerIs9x :

= OSversion.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;

End;

以上直接调用API函数,已在Windows单元中被声明。

functionLoadLibrary16(LibraryName:

PChar):

externalkernel32index35;

procedureFreeLibrary16(HInstance:

THandle);

externalkernel32index36;

functionGetProcAddress16(Hinstance:

ProcName:

Pointer;

externalkernel32index37;

functionTWinResMonitor.GetFr

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 初中教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1