小李子

小小的蜗牛,慢慢的爬。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

让你的程序支持插件

Posted on 2012-03-27 16:47  ljl_falcon  阅读(325)  评论(0编辑  收藏  举报

源码 http://www.aidelphi.com/6to23/docu/plugin.zip      

相信各位应该都用过WinAmp,也相信它对插件的支持是它最终流行起来的主要原因。能不能让我们自已的程序也支持插件呢,以面我们就用Delphi来为我们编第一个支持插件的程序。

对于一般用户来说,插件就是一个DLL文件,但与一般DLL不同的是,插件支持对主程序功能的扩展,主程序没有插件也一样能运行,但一般的DLL大多数是主程序不可缺少的部份。当需要经常为客户更新应用程序版本时,插件也许是你不错有选择。

首先,在编写应用程序之前需要清楚应用程序本身可以完成什么主要功能,需要扩展什么样的功能,因为插件本身是通过主程序提供的特定接口来与主程序进行行交互的,在编写主程序时你需要确定这些接口。在本例中,定义了两个接口,一个完成插件的初始化,主要是指选择并载入相应的图片,另一个是在主Form重绘时绘制出相应的图片,其中第二个接口需要传递画布句柄和绘制区域。

以下是主程序主Form interface段的定义:

  TPluginDescribe = Function:Boolean; stdcall;

  TPluginDrawDescribe = procedure(DC:HDC;Rect:TRect); stdcall;

  TForm1 = class(TForm)

    ListBox1: TListBox;

    procedure FormCreate(Sender: TObject);

    procedure ListBox1DblClick(Sender: TObject);

    procedure FormPaint(Sender: TObject);

    procedure FormClose(Sender: TObject; var Action: TCloseAction);

  private

    LibHandle: HMODULE;

    DwarProc: TPluginDrawDescribe;

    procedure LoadPlugins;

    procedure LoadPlugin(sr: TSearchRec);

    { Private declarations }

  public

    { Public declarations }

  end;

其中定义了一个过程类型和一个函数类型,其中过程类型有两个变量,一个HDC类型的画布句柄,另一个是TRECT类型的绘制区域。TForm1有两个私有字段LibHandleDwarProc用于保存装入的插件DLL的句柄和绘图过程句柄。

在主Form创建时检查是否有插件程序,就列出可用插件,TForm1OnCreate事件句柄FormCreate实现代码如下:

procedure TForm1.FormCreate(Sender: TObject);

begin

  LibHandle:=0;//初始化LibHandle的值。

  LoadPlugins;//调入LoadPlugins过程。

end;

以下是LoadPlugins的实现代码:

procedure TForm1.LoadPlugins;

var

  sr: TSearchRec;

  path: string;

  Found: Integer;

begin

  path := ExtractFilePath(Application.Exename);

  try

    Found := FindFirst(path + '*.DLL', 0, sr);

    while Found = 0 do//查找文件。

    begin

      LoadPlugin(sr);

      Found := FindNext(sr);

    end;

  finally

    FindClose(sr);

  end;

end;

查找当前路径下所有扩展名为DLL的文件,如果找到则检查是否为本例插件。

检查插件文件的过程LoadPlugin实现代码如下:

procedure TForm1.LoadPlugin(sr: TSearchRec);

var

  iLibHandle: HMODULE;

  pDescribeProc: TPluginDescribe;

  pDwarProc: TPluginDrawDescribe;

begin

  iLibHandle := LoadLibrary(Pchar(sr.Name));

  if iLibHandle <> 0 then

  begin

    try

      pDescribeProc := GetProcAddress(iLibHandle, 'loadpicture');

      pDwarProc := GetProcAddress(iLibHandle, 'drawpicture');

      if Assigned(pDescribeProc) and Assigned(pDwarProc) then

        ListBox1.Items.Add(sr.Name);

    finally

      FreeLibrary(iLibHandle);

    end;

  end else

    ShowMessage('loading Dll file error!');

end;

首先动态装入插件库使用LoadLibrary API函数,如果其中有接口过程,则将文件名加入到ListBox1中。其中GetProcAddress是获得DLL中某个例程的地址。不使用DLL时用FreeLibrary卸载。

ListBox1的双击事件中才真正启动插件:

procedure TForm1.ListBox1DblClick(Sender: TObject);

var pDescribeProc: TPluginDescribe;

begin

  if ListBox1.ItemIndex<>-1 then

  begin

    if LibHandle<>0 then

      FreeLibrary(LibHandle);

    DwarProc:=nil;

    LibHandle := LoadLibrary(PChar(ListBox1.Items[ListBox1.ItemIndex]));

    if LibHandle <> 0 then

    begin

      try

        pDescribeProc := GetProcAddress(LibHandle, 'loadpicture');

        if pDescribeProc then

        begin

          DwarProc := GetProcAddress(LibHandle, 'drawpicture');

          Invalidate;

        end;

      Except

        FreeLibrary(LibHandle);

        LibHandle:=0;

      end;

    end

    else

      ShowMessage('loading Dll file error!');

  end;

end;

//这段代码主要是先调用装入图片的插件过程,如果成功,则取绘图片的过程地址。只有LibHandle<>0和给DwarProc赋值之后才算真正启动了插件。

TForm1OnPaint事件中调用DwarProc过程,启动插件中的绘图过程:

procedure TForm1.FormPaint(Sender: TObject);

begin

  try

    if (LibHandle<>0) and Assigned(DwarProc) then

      DwarProc(Self.Canvas.Handle,Self.ClientRect);

  Except

    if LibHandle<>0 then

    begin

      FreeLibrary(LibHandle);

      LibHandle:=0;

    end;

  end;

end;

最后别忘了在TForm1OnClose事件中卸载DLL

 

完成后运行,发现ListBox1中什么也没有,因为插件程序还没有建立。

本例共有两个插件,一个完成图片在主Form上的平铺绘制,另一个完成缩放绘制。

插件程序主单元如下:

library PlugIn1;

 

uses

  SysUtils,

  Classes,

  PlugInUnit1 in 'PlugInUnit1.pas';

 

exports loadpicture,drawpicture;

//导出两个过程。

begin

end.

两个过程的定义和实现代码在单元PlugInUnit1中:

unit PlugInUnit1;

 

interface

Uses Graphics,extdlgs,Windows;

 

Function loadpicture:Boolean;

procedure drawpicture(DC:HDC;Rect:TRect);export; stdcall;

 

Var Picture:TPicture;

 

implementation

 

procedure drawpicture(DC:HDC;Rect:TRect);

Var cCanvas:TCanvas;

begin

  if (Picture<>nil) and not Picture.Graphic.Empty then

  begin

    cCanvas:=TCanvas.Create;

    try

      cCanvas.Handle:=DC;

      cCanvas.StretchDraw(Rect,Picture.Graphic);

    finally

      cCanvas.Free;

    end;

  end;

end;

 

Function loadpicture:Boolean;

begin

  Result:=False;

  Picture:=TPicture.Create;

  with TopenpictureDialog.Create(nil) do

  begin

    try

      if Execute then

      begin

        Picture.LoadFromFile(FileName);

        Result:=True;

      end;

    finally

      Free;

    end;

  end;

end;

 

end.

上面的插件完成图片的缩放绘制,另一个插件的代码除单元名和drawpicture的实现不同之外,其余均是相同的。在此不重复。相信读者也能自行完成。笔者的主页http://www.aidelphi.com上有该例程最完整的代码。