Delphi中编写无输出函数名的DLL文件

用 Delphi 用长了,总是发现,有些和 MS 不同的地方。例如,MS 的公开库中,常常隐藏了许多重要函数,这些函数在系统中常常有起着非常巨大的作用。一旦知道如何调用,可以给自己的应用程序提供很强的功能和很大的灵活性。但,这些函数通常又没有函数名(即使用 ExeScope 查看 DLL 文件的导出表也看不出函数意义),仅仅只有一个序号来表示。有时候我又自己想,为什么我在写程序的时候不能学学 MS 隐藏一些自己不希望公开的函数呢?

其实用 Delphi 写 DLL 的时候,使用简单的技巧就可以实现隐藏函数名的效果。让我们来看看下面这个 DLL 源码:

library proDll;

uses

Windows;

{$R *.res}

procedure ShowMessageA(hWnd: HWND); stdcall;

begin

MessageBox(hWnd, '您调用的是 ShowMessageA 函数', 'DLL 函数信息',

MB_ICONINFORMATION);

end;

procedure ShowMessageB(hWnd: HWND); stdcall;

begin

MessageBox(hWnd, '您调用的是 ShowMessageB 函数', 'DLL 函数信息',

MB_ICONINFORMATION);

end;

exports

ShowMessageA index 1 name '',

ShowMessageB index 2 name '';

begin

end.

注意看 exports 部分,用 index 关键字指定输出函数的序号,后面紧跟一个 name 关键字指明输出函数名称。关键就在这里,name 后面是一个空字符串,这样就给函数生成了一个空字符串名。实际效果既是隐藏了输出函数的名称。是不是很容易呢?

那么我们怎样调用这样的输出函数呢?由于没有了函数名,我们调用起来会显得和以前不一样。其实也不用担心,调用同样非常简单。我下面就静态调用和动态调用制作了两个工程,源码如下:

静态调用例子:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

Button2: TButton;

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

procedure ShowMessageA(hWnd: HWND); stdcall; external 'proDll.dll' index 1;

procedure ShowMessageB(hWnd: HWND); stdcall; external 'proDll.dll' index 2;

procedure TForm1.Button1Click(Sender: TObject);

begin

ShowMessageA(Handle);

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

ShowMessageB(Handle);

end;

end.

动态调用的例子:

unit Unit2;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TForm2 = class(TForm)

Button1: TButton;

Button2: TButton;

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form2: TForm2;

implementation

{$R *.dfm}

type

TDllShowMessageFunc = procedure (hWnd: HWND); stdcall;

var

hDllHandle: THandle;

ShowMessageA, ShowMessageB: TDllShowMessageFunc;

procedure LoadFuncDll;

begin

if hDllHandle = 0 then

begin

hDllHandle := LoadLibrary('proDll.dll');

if hDllHandle = 0 then

raise Exception.Create('proDll.dll 加载失败');

try

{

lpProcName: the second argument of function GetProcAddress

Points to a null-terminated string containing the function name,

or specifies the function's ordinal value. If this parameter is

an ordinal value, it must be in the low-order word; the high-order

word must be zero.

}

@ShowMessageA := GetProcAddress(hDllHandle, Pointer(HiWord(0) or LoWord(1)));

if @ShowMessageA = nil then

raise Exception.Create('proDll.dll 中没有输出 ShowMessageA 函数');

@ShowMessageB := GetProcAddress(hDllHandle, Pointer(HiWord(0) or LoWord(2)));

if @ShowMessageB = nil then

raise Exception.Create('proDll.dll 中没有输出 ShowMessageB 函数');

except

FreeLibrary(hDllHandle);

hDllHandle := 0;

raise;

end;

end;

end;

procedure FreeFuncDll;

begin

if hDllHandle <> 0 then

begin

FreeLibrary(hDllHandle);

hDllHandle := 0;

@ShowMessageA := nil;

@ShowMessageB := nil;

end;

end;

procedure TForm2.Button1Click(Sender: TObject);

begin

if @ShowMessageA = nil then LoadFuncDll;

ShowMessageA(Handle);

end;

procedure TForm2.Button2Click(Sender: TObject);

begin

if @ShowMessageB = nil then LoadFuncDll;

ShowMessageB(Handle);

end;

initialization

// do nothing

finalization

FreeFuncDll;

end.