我们的插件外壳程序与普通应用程序之间的唯一不同就在于工程源文件中出现在uses子句中的Sharemem单元和加载插件文件的代码。任何在自身与子DLL之间传递字符串参数的应用? 都需要Sharemem单元,它是DelphiMM.dll(Delphi提供该文件)的接口。要测试这个外壳,需要将DelphiMM.dll文件从Delphi/Bin目录复制到path环境变量所包含的路径或者应用程序所在目录中。发布最终版本时也需要同时分发该文件。 插件通过LoadPlugins过程载入到这个测试外壳中,这个过程在主窗口的FormCreate事件中调用,见图2。该过程使用FindFirst和FindNext函数在应用程序所在目录中查找插件文件。找到一个文件以后,就使用图3所示的LoadPlugins过程将其载入。 { 在应用程序目录下查找插件文件 } PRocedure TfrmMain.LoadPlugins; var sr: TSearchRec; path: string; Found: Integer; begin path := ExtractFilePath(application.Exename); try Found := FindFirst(path + cPLUGIN_MASK, 0, sr); while Found = 0 do begin LoadPlugin(sr); Found := FindNext(sr); end; finally FindClose(sr); end; end;
{ 加载指定的插件 DLL. } procedure TfrmMain.LoadPlugin(sr: TSearchRec); var Description: string; LibHandle: Integer; DescribeProc: TPluginDescribe; begin LibHandle := LoadLibrary(Pchar(sr.Name)); if LibHandle $#@60;$#@62; 0 then begin DescribeProc := GetProcAddress(LibHandle, cPLUGIN_DESCRIBE); if Assigned(DescribeProc) then
begin DescribeProc(Description); memPlugins.Lines.Add(Description); end else begin MessageDlg(’File "’ + sr.Name +’" is not a valid plug-in.’, mtInformation, [mbOK], 0); end; end else MessageDlg(’An error occurred loading the plug-in "’ + sr.Name + ’".’, mtError, [mbOK], 0); end;
我们已经创建好了父应用程序,现在该轮到创建我们希望加载的插件了。插件文件是一个标准的Delphi DLL,所以我们从Delphi IDE中创建一个新DLL工程,保存它。由于导出的插件函数将用到字符串参数,所以要在工程的uses子句中把Sharemen单元放在最前面。图4列出的就是我们这个简单插件的工程源文件。 uses Sharemem, SysUtils, Classes, main in ’main.pas’;
{$E plg.}
eXPorts DescribePlugin;
begin
end.
虽然插件是一个DLL文件,但是没有必要一定要给它一个.DLL的扩展名。实际上,一个原因就足以让我们有理由改变扩展名:当父应用程序寻找要加载的文件时,新的扩展名可以作为特定的文件掩模。通过使用别的扩展名(我们的例子使用了*.plg),你可以在一定程度上确信应用程序只会载入相应的文件。编译指示字$X可以实现这个改变,也可以通过Project Options对话框的Application页来设置扩展名。 第一个例子插件的代码是很简单的。图5显示了包含在一个新单元中的代码。注重,DescribePlugin原型与外壳应用程序中的TpluginDescribe类型相一致,使用附加的export保留字指定该过程将被导出。被导出的过程名称也将会出现在主工程源代码的exports段中(在图4中列出)。 unit main;