unit Extensions;

interface

uses Windows, SysUtils, MainUnit, Init, Plan, SharedData, MiscRoutines;

const
  MaxModule = 128;
  MaxPlugIn = 256;

type
  pIPlugIn = ^IPlugIn;
  IPlugIn = object
    // Строка меню
    function MenuName:PChar;virtual;stdcall;abstract;
    // Дееспособность
    function CanDo:LongBool;virtual;stdcall;abstract;
    // Основная функция
    function PluginMain:pchar;virtual;stdcall;abstract;
    procedure PluginMainThunk(Sender:TObject);
  end;

  TPLugInArray          = array[0..MaxPlugIn-1] of pIPlugIn;
  PPlugInArray          = ^TPLugInArray;

  TPlugIns              = class(TObject)
  private
    ModulesCount        : integer;
    Modules             : array[0..MaxModule-1] of HModule;
    InterfaceCount      : integer;
    Interfaces          : TPLugInArray;
    function            GetCount:integer;
    function            GetPlugIn(Ref:integer):pIPlugIn;
    function            AppendModule(LibName:string):Boolean;
  public
    constructor         Create;
    destructor          Destroy;override;
    function            FindPlugIns(Path:string):integer;
    property            Count:integer read GetCount;
    property            Items[Index : integer] : pIPlugIn read GetPlugIn;
  end;

var
  PlugIns : TPlugIns;

implementation

procedure IPlugIn.PluginMainThunk(Sender:TObject);
begin
  PluginMain;
end;

function TPlugIns.GetCount;
begin
  Result:=InterfaceCount;
end;

function TPlugIns.GetPlugIn;
begin
  if((Ref<0)or(Ref>=InterfaceCount)) then
    Result:=nil
  else
    Result:=Interfaces[Ref];
end;

constructor TPlugIns.Create;
begin
  memZeroMemory(@Modules,MaxModule*sizeof(HModule));
  memZeroMemory(@Interfaces,MaxPlugIn*sizeof(pIPlugIn));
  InterfaceCount:=0;
end;

destructor TPlugIns.Destroy;
var
  i : integer;
begin
  for i:=0 to MaxModule-1 do
  begin
    if(Modules[i]<>0) then
      FreeLibrary(Modules[i]);
  end;
  inherited;
end;

const
  PLGENUM_OK          =1;
  PLGENUM_CANCEL      =0;

type
  // Функции, через которые передается указатель на сервер
  TSetMainServer = procedure(MainServer:IMathPlan);stdcall;
  TGetMainServer = function:IMathPlan;stdcall;
  // Функция обратного вызова для перечисления
  TEnumPlugInsProc    = function(PlugIn:pIPlugIn):integer;stdcall;
  // Перечислитель
  TPlugInsEnumerate   = function(EnumProc:TEnumPlugInsProc):integer;stdcall;

var
  CurrentCount, MaxCount : pdword;
  PlugInsArray : PPLugInArray;

function EnumCallBack(PlugIn:pIPlugIn):integer;stdcall;
begin
  if(CurrentCount^<=MaxCount^) then
  begin
    PlugInsArray^[CurrentCount^]:=PlugIn;
    CurrentCount^:=CurrentCount^+1;
    Result:=PLGENUM_OK;
  end
  else
    Result:=PLGENUM_CANCEL;
end;

function TPlugIns.AppendModule(LibName:string):Boolean;
var
  hlib   : HModule;
  sets   : TSetMainServer;
  main   : TPlugInsEnumerate;
  stub   : integer;
begin
  Result:=False;
  if(InterfaceCount>=MaxPlugIn) then exit;
  hlib:=Windows.LoadLibrary(PChar(LibName));
  if(hlib=0) then
  begin
    Error('Ошибка загрузки дополнения "'+LibName+'".');
    exit;
  end;
  sets:=Windows.GetProcAddress(hlib,'SetMainServer');
  if(@sets=nil) then
  begin
    Error('Ошибка загрузки дополнения "'+LibName+'":'+#13+
      'не найдена точка входа в процедуру "SetMainServer()".');
    FreeLibrary(hlib);
    exit;
  end;
  main:=Windows.GetProcAddress(hlib,'PlugInsEnumerate');
  if(@main=nil) then
  begin
    Error('Ошибка загрузки дополнений "'+LibName+'":'+#13+
      'не найдена точка входа в процедуру "PlugInsEnumerate()".');
    FreeLibrary(hlib);
    exit;
  end;
  CurrentCount:=@InterfaceCount;
  stub:=MaxPlugIn;
  MaxCount:=@stub;
  PlugInsArray:=@Interfaces;
  main(EnumCallBack);
  Modules[ModulesCount]:=hlib;
  sets(MainForm.MainPlan);
  ModulesCount:=ModulesCount+1;
  Result:=True;
end;

function TPlugIns.FindPlugIns;
var
  SearchRec : TSearchRec;
  Found     : integer;
begin
  Found:=FindFirst(Path+'\*.dll',faAnyFile,SearchRec);
  while Found = 0 do
  begin
    IniForm.Msg('Дополнение: '+Path+'\'+SearchRec.Name);
    AppendModule(Path+'\'+SearchRec.Name);
    Found:=FindNext(SearchRec);
  end;
  FindClose(SearchRec);
  Result:=InterfaceCount;
end;

end.
