Динамическая подгрузка RTTI компонентов-библиотек

отредактировано 07:46 Раздел: FastScript
Привет всем !

Скачал потестировать FastScript, и сразу озадачился вот по какому поводу!
Есть набор компонентов Scripter Studio От TMS Software - по реализации очень похож на Fast Script. Там из обычных pas модулей с компонентами с помощью программы импортера (или вручную - но о-очень долго ;) ) формируются файлы-обертки с реализацией класса TatLibrary - который содержит реализацию методов и свойств компонентов.
Файл обертку можно указать в uses разделе Delphi иодуля и вызвав Scripter.AddLibrary(TatSomeLibrary) Подключить ее к компоненту-скриптеру.
Мне не хотелось увеличивать объем исполняемого файла, включая в uses все модули-обертки, которые могут и не понадобиться, и я поступил следующим образом - подключал библиотеки в Run-Time если они были необходимы.
Создавал DLL, из которой экспортировалась функция, принимающая как Var-параметр объект скриптера, и добавляющая к нему библиотеку. Для каждого модуля своя DLL - stdctrls.dll, extctrls.dll и т.д.

А есть ли в Fast Script возможность также организовать динамическое подключение только нужных компонент. Чтобы не подключать прям в дизайн тайме все RTTI компоненты ???

Кстати, разработчики !!
Вы Молодцы - TMS'овцы никак не могут (или не хотят ;) ) реализовать функциональность USES в своих скриптах !! И быстрых исправлений от них фиг добъешься !
Так держать !

Комментарии

  • отредактировано 07:46
    написал:
    Мне не хотелось увеличивать объем исполняемого файла, включая в uses все модули-обертки, которые могут и не понадобиться, и я поступил следующим образом - подключал библиотеки в Run-Time если они были необходимы.

    А смысл, все равно ведь все библиотеки за собой таскать...
    Сделать, конечно, можно (передавать в экспортируемую ф-ю fsGlobalUnit и добавлять в него нужные вещи). Не пробовал, но по идее должно работать.
    написал:
    Вы Молодцы - TMS'овцы никак не могут (или не хотят  ) реализовать функциональность USES в своих скриптах !!

    Сейчас тут скажут, что хотя uses и есть, но работает криво, и будут правы ;)
  • отредактировано 07:46
    AlexTZ
    Да сказать-то скажут, но лиха беда начало ;) !! Я, кстати, уже почитал по поводу нападок на реализацию USES !

    А по поводу передачи в функцию fsGlobalUnit - это точно, вчера видимо тормозил и невнимательно поглядел на реализацию модулей fs_xxxxRTTI ;) . Сейчас буду пробовать и если все ок, напишу в этом топике.
    написал:
    А смысл, все равно ведь все библиотеки за собой таскать...

    Библиотеки таскать-то придется, но во первых у меня там идет сборка с пакетами и DLL-ки получаются по 6-25 кб, а во вторых грузятся-то не все, а только нужные, а мне как раз и нужно было не просто избавиться от лишнего веса "exe", а скорее от загрузки в память ненужного груза компонентов, которые не будут использоваться в скрипте.

    Сейчас если получится у меня то же, что и с компонентами от TMS - попробую написать парсер-импортер для автоматической генерации fs_xxxxxRTTI модулей. А то руками что-то сильно напрягаюсь такие вещи делать ! ;)

    Спасибо за ответ !! Удачи !!
  • отредактировано 07:46
    AlexTZ
    написал:
    Сделать, конечно, можно (передавать в экспортируемую ф-ю fsGlobalUnit и добавлять в него нужные вещи). Не пробовал, но по идее должно работать.
    Однако fsGlobalUnit функция и как var параметр ее передать не выйдет !! ;)
    Нет у разработчиков в планах добавить компоненту TfsScript какую-нибудь функцию типа AddLibrary, которая в качестве параметра принимала бы абстрактный класс TFunction, а все TFunction в модулях fs_xxxxxxRTTI наследовать от абстрактного класса ?
    ИМХО удобно было бы !
    С уважением и почтением к столь мудрым разработчикам !!
  • отредактировано 07:46
    fsGlobalUnit возвращает экземпляр TfsScript, который и надо передавать в dll.
  • отредактировано March 2004
    AlexTZ

    Передавал !!

    Отрабатывает и основной EXE и DLL, и внутри DLL с переданным экземпляром fsScript проходит все ок : классы, константы и все остальное добавляются !!

    Конструктор в файле fs_ixxxxrtti исправил так чтобы работал не c fsGlobalUnit из подключенного модуля, а с переданным экземпляром. Отключил разделы initialization и finalization, Functions создаю вручную.
    Объявление класса TFunctions и переменную Functions вынес в interface, чтоб видно их было !!

    Вот такой процедуркой работаю в DLL с переданным из основной программы fsGlobalUnit'ом :
    procedure LoadRTTI(AfsScript : TfsScript); stdcall;
    begin
     if Assigned(AfsScript) then 
      begin
       Functions.Create(AfsScript);
      end;
    end;
    

    Однако хоть и без ошибок выполняется код подгрузки, все равно далее, при использовании, например fs_iclassesrtti и его "подключении" через DLL и последующей попытке создать скажем экземпляр TStringList вылазит Exception с сообщением о том, что тип TStringList не найден ! ;)
  • отредактировано 07:46
    Таки я не понял, есть парсер, который генерит файлы для подлючения "нестандартных" компонентов или нет?
  • отредактировано 07:46
    Нет, только ручками.
  • отредактировано 07:46
    Подскажите где я не прав
    Написал обертку…
    unit fs_doa;
    
    interface
    Uses DB, Variants, OracleData, Oracle, fs_iinterpreter;
    
    type
      TDOAFunctions = class
      private
        FGlobalUnit : TfsScript;
    …………..
      public
        constructor Create( GlobalUnit : TfsScript );
        destructor Destroy; override;
      end;
    
    var DOAFunctions: TDOAFunctions;
    
    implementation
    
    constructor TDOAFunctions.Create( GlobalUnit : TfsScript );
    begin
      Inherited Create;
      FGlobalUnit := GlobalUnit;
      with FGlobalUnit do
      begin
        AddedBy := Self;
        with AddClass(TOracleDataset, 'TDataSet') do
        begin
    …………..
        end;
    …………..
        AddedBy := nil;
      end;
    end;
    
    destructor TDOAFunctions.Destroy;
    begin
      if ( Assigned( FGlobalUnit )) then
           FGlobalUnit.RemoveItems( Self );
      Inherited Destroy;
    end;
    
    …………..
    
    //initialization
    //  DOAFunctions := TDOAFunctions.Create;
    //finalization
    //  DOAFunctions.Free;
    end.
    

    Написал каркас библиотеки
    library script_doa;
    uses
      fs_iinterpreter,
      fs_doa in 'fs_doa.pas';
    
    {$R *.res}
    
    procedure PluginInit( GlobalUnit : TfsScript );
    begin
      DOAFunctions := TDOAFunctions.Create( GlobalUnit );
    end;
    
    procedure PluginDone;
    Begin
      DOAFunctions.Free;
    End;
    
    exports PluginInit, PluginDone;
    
    end.
    

    После компиляции библиотеки пытаюсь вызывать
    procedure TForm1.btnExecClick(Sender: TObject);
    Var
      LibHandle: THandle;
      PluginInit : procedure ( GlobalUnit : TfsScript );
      PluginDone : procedure;
    
    begin
      with Script do
      begin
        Clear;
        Lines.Clear;
        Lines.AddStrings( ScriptMemo.Lines );
        Parent := fsGlobalUnit;
    
        ShowMessage( IntToStr( Parent.Count )); // запоминаю кол-во конструкции
        LibHandle := LoadLibrary( 'script_doa.dll' );
        try
        if LibHandle >= 32 then
        begin
          PluginInit := GetProcAddress( LibHandle, 'PluginInit');
          if ( Assigned( PluginInit )) then
             PluginInit( Parent );
        end;
        ShowMessage( IntToStr( Parent.Count )); // вижу, что конструкций стало больше… 
    
    // Отрабатывает и показывает «Class TOracleDataSet»
        if ( Assigned( Find( 'TOracleDataSet' ))) then
        begin
          ShowMessage( Find( 'TOracleDataSet' ).GetFullTypeName );
        end;
    
    // Не отрабатывает
        if ( Assigned( FindClass( 'TOracleDataSet' ))) then
        begin
          ShowMessage( FindClass( 'TOracleDataSet' ).GetFullTypeName );
        end;
    
    // Происходит ошибка компиляции Unknown type: 'TOracleDataSet’
        if Script.Compile then
        Begin
           Script.Execute;
        End Else
           ShowMessage( Script.ErrorMsg );
    
        finally
          PluginDone := GetProcAddress( LibHandle, 'PluginDone');
          if ( Assigned( PluginDone )) then PluginDone;
          FreeLibrary( LibHandle );
        end;
    // Вычищается корректно к исходному кол-ву известных интерпритатору конструкции 
        ShowMessage( IntToStr( Parent.Count ));
      end;
    end;
    

    Доктор… где я не прав… как же подгружать компоненты динамически? ;)
  • отредактировано 07:46
    Работает ли это все, если не использовать dll, а поместить все в один проект?
  • отредактировано 07:46
    Да... конечно работает
    procedure TForm1.btnExecClick(Sender: TObject);
    begin
      with Script do
      begin
        Clear;
        Lines.Clear;
        Lines.AddStrings( ScriptMemo.Lines );
        Parent := fsGlobalUnit;
    
        try
          //Уже создаю просто без dll
          DOAFunctions := TDOAFunctions.Create( Parent );
    
          AddedBy := Self;
          AddForm( Form1 );
          AddComponent( DM_Script.Ses );
          AddComponent( DM_Script );
          AddedBy := nil;
    
          if ( Assigned( Find( 'TOracleDataSet' ))) then
          begin
            ShowMessage( Find( 'TOracleDataSet' ).GetFullTypeName );
          end;
    
          if ( Assigned( FindClass( 'TOracleDataSet' ))) then
          begin
            ShowMessage( FindClass( 'TOracleDataSet' ).GetFullTypeName );
          end;
    
          if Script.Compile then
          Begin
             Script.Execute;
          End Else
             ShowMessage( Script.ErrorMsg );
    
        finally
          DOAFunctions.Free;
        end;
      end;
    end;
    

    В пошаговом режиме видно, что при варианте с dll в процедуре
    function TfsScript.FindClass(const Name: string): TfsClassVariable;
    var
      Item:  TfsCustomVariable;
    begin
      Item := TfsClassVariable(Find(Name));
      if (Item <> nil) and (Item is TfsClassVariable) then
        Result := TfsClassVariable(Item) else
        Result := nil
    end;
    

    Не выполняется условие (Item is TfsClassVariable) и найденный Item не TfsClassVariable... пробовал просто убрать условие... падает в экзепшн... как же быть... извелся весь? ;)
  • отредактировано 07:46
    Очевидно, в dll информация о классах (RTTI) не совпадает с RTTI основного приложения.
  • отредактировано 07:46
    Сейчас только попробовал сделать тестовую длл-ку. Действительно, оператор IS в dll не работает (передаю в dll ссылку на форму основного приложения и пытаюсь проверять if Form is TForm).
  • отредактировано 07:46
    Следовательно это происходит по причине

    For most applications, packages provide greater flexibility and are easier to create than DLLs. However, there are several situations where DLLs would be better suited to your projects than packages:

    Your code module will be called from non-Delphi applications.
    You are extending the functionality of a Web server.
    You are creating a code module to be used by third-party developers.
    Your project is an OLE container.

    However, if your application includes VisualCLX, you must use packages instead of DLLs. Only packages can manage the startup and shut down of the Qt shared libraries.

    You cannot pass Delphi runtime type information (RTTI) across DLLs or from a DLL to an executable. If you pass an object from one DLL to another DLL or an executable, you will not be able to use the is or as operators with the passed object. This is because the is and as operators need to compare RTTI. If you need to pass objects from a library, use packages instead, as these can share RTTI. Similarly, you should use packages instead of DLLs in Web Services because they are rely on Delphi RTTI.

    последний абзац? хм... а что такое packages instead?
  • отредактировано 07:46
    Типа - не юзайте dll, юзайте пакеты.
  • отредактировано 07:46
    Не получается
    package script_doa;
    
    {$R *.res}
    {$ALIGN 8}
    {$ASSERTIONS ON}
    {$BOOLEVAL OFF}
    {$DEBUGINFO ON}
    {$EXTENDEDSYNTAX ON}
    {$IMPORTEDDATA ON}
    {$IOCHECKS ON}
    {$LOCALSYMBOLS ON}
    {$LONGSTRINGS ON}
    {$OPENSTRINGS ON}
    {$OPTIMIZATION ON}
    {$OVERFLOWCHECKS OFF}
    {$RANGECHECKS OFF}
    {$REFERENCEINFO ON}
    {$SAFEDIVIDE OFF}
    {$STACKFRAMES OFF}
    {$TYPEDADDRESS OFF}
    {$VARSTRINGCHECKS ON}
    {$WRITEABLECONST OFF}
    {$MINENUMSIZE 1}
    {$IMAGEBASE $400000}
    {$IMPLICITBUILD ON}
    
    requires
      rtl,
      vcl,
      dbrtl,
      doa34d7,
      dcldb,
      vcldesigner,
      designide,
      vclactnband,
      vclx,
      designdgm,
      dclstd,
      vcldb,
      dsnap,
      fsx;
    
    contains
      fs_doa in 'fs_doa.pas';
    
    end.
    

    В fs_doa.pas добавляю две публичные функции
    unit fs_doa;
    …………..
    
    procedure PluginInit( GlobalUnit : TfsScript );
    procedure PluginDone;
    
    implementation
    
    procedure PluginInit( GlobalUnit : TfsScript );
    begin
      DOAFunctions := TDOAFunctions.Create( GlobalUnit );
    end;
    
    procedure PluginDone;
    Begin
      DOAFunctions.Free;
    End;
    …………..
    end.
    

    Выдираю имена функций
    TDUMP.EXE -m script_doa.bpl
    

    Переписал вызов
    procedure TForm1.btnExecClick(Sender: TObject);
    Var
      LibHandle  : HMODULE;
      PluginInit : procedure ( GlobalUnit : TfsScript );
      PluginDone : procedure;
    
    begin
      with Script do
      begin
        Clear;
        Lines.Clear;
        Lines.AddStrings( ScriptMemo.Lines );
        Parent := fsGlobalUnit;
    
        try
    
        ShowMessage( IntToStr( Parent.Count ));// запоминаю кол-во конструкции
        LibHandle := LoadPackage( 'script_doa.bpl' );
        PluginInit := GetProcAddress( LibHandle, '@Fs_doa@PluginInit$qqrp25Fs_iinterpreter@TfsScript');
        if ( Assigned( PluginInit )) then
           PluginInit( Parent );
        ShowMessage( IntToStr( Parent.Count )); // вижу, что конструкций стало больше… 
    
    // Отрабатывает и показывает «Class TOracleDataSet»
        if ( Assigned( Find( 'TOracleDataSet' ))) then
        begin
          ShowMessage( Find( 'TOracleDataSet' ).GetFullTypeName);
        end;
    
    // Не отрабатывает
        if ( Assigned( FindClass( 'TOracleDataSet' ))) then
        begin
          ShowMessage( FindClass( 'TOracleDataSet' ).GetFullTypeName + '!' );
        end;
    
        finally
           PluginDone := GetProcAddress( LibHandle, '@Fs_doa@PluginDone$qqrv' );
           PluginDone;
           UnloadPackage( LibHandle );
        end;
    // Вычищается корректно к исходному кол-ву известных интерпритатору конструкции 
        ShowMessage( IntToStr( Parent.Count ));
      end;
    

    Может где RegisterClass надо вызывать? или что делать? я совершенно запутался... помогите ;)
  • отредактировано 07:46
    Я полагаю, основная программа должна быть также скомпилирована с использованием пакетов. Иначе какой смысл пакетов вообще?
  • отредактировано 07:46
    Да действительно… проверил… собираем основную программу с рантайм пакетами… включая и этот… когда необходим подгружаем его передаем в PluginInit… fsGlobalUnit и RegisterClass, чтобы зарегистрировать в пакете все классы… и все работает
    Но только не этого я хотел добиться… сборка с рантайм пакетами противоречит концепции моей разработки… а объявление пакета расширения еще на этапе сборки… не дает универсальной плагинной архитектуры системы… которой я хотел добиться… много хотел… не подумав ;) но все равно спасибо, что помогли расставить все на свои места ;)

Оставить комментарий

Многофункциональный текстовый редактор. Чтобы отредактировать стиль параграфа, нажмите TAB, чтобы перейти к меню абзаца. Там вы можете выбрать стиль. По умолчанию не выбран ни один стиль. Когда вы выберете текст, появится встроенное меню форматирования. Нажмите TAB, чтобы войти в него. Некоторые элементы, такие как многофункциональные вставки ссылок, картинок, индикаторов загрузки и сообщений об ошибок могут быть вставлены в редактор. Вы можете перемещаться по ним, используя стрелки внутри редактора и удалять с помощью клавиш delete или backspace.