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 .