FoxWMulder
FoxWMulder

Reputation: 9

Creating a component from a DLL, runtime

Decided to migrate from Delphi 2007 to Delphi 11 Alexandria. I had DLLs made with component creation from them. In the program, I dynamically load the DLL, a component is created in the DLL. Code that worked without issue on Delphi 2007 does not work on Delphi 11 Alexandria. Errors are constantly different, if I solve one error, another error appears. I can't find a solution. Please tell me how to create a component from a DLL.

My old code:

DLL:

library PascalHighlighter;

uses
  Forms,
  SynEditHighlighter, SynHighlighterPas, SynMemo;

{$R *.res}

var Component:TSynPasSyn;

type TSynPasSynClass = class of TSynPasSyn;

procedure PascalHighlighter_Create(Form:TForm; ComponentNum:Word);
begin
if Component=nil then begin
 Component := TSynPasSynClass.Create(Form);
 Component.Name:='SynPasSyn1';
end;
TSynmemo(Form.Components[ComponentNum]).Highlighter:=Component;
Component.CommentAttri.Foreground:=$00007F00;
Component.KeyAttri.Foreground:=$00A40000;
Component.StringAttri.Foreground:=$00FE0000;
end;

exports PascalHighlighter_Create;


begin
end.

Load DLL:

type

TDLLComponent = function(Form:TForm; ComponentNum: Word): Word;

var

PascalHighlighter_Create: TDLLComponent;
dll_PascalHighlighter: Thandle;

procedure TForm1.ButtinClick(Sender: TObject);
begin
if Fileexists(dirplugins+'PascalHighlighter.dll') then begin
 dll_PascalHighlighter:= LoadLibrary(PChar(dirplugins+'PascalHighlighter.dll'));
 @PascalHighlighter_Create:= GetProcAddress(dll_PascalHighlighter, 'PascalHighlighter_Create');
end;
if Assigned(PascalHighlighter_Create) then begin
 PascalHighlighter_Create(Form1,(Form1.Findcomponent('Synmemo1') as TSynmemo).ComponentIndex);
end;

end;

I tried to create an ordinary Memo from a DLL. Another error, but it doesn't work either.

I need to understand how to create a SynPasSyn1 component from a DLL and connect it to SynMemo. Or at least using the example of any other component, I can figure it out myself further.

Here is an example of creating a Memo. Why does it give an error " cannot assign a TFont to a TFont" and how to solve it?

library Mymemo;

uses
  Vcl.Forms, Vcl.StdCtrls;

{$R *.res}

var Component:TMemo;

procedure Mymemo_Create(Form:TForm); stdcall;
begin
 Component := TMemo.Create(Form);
 Component.Name:='Memo1';
with Component do begin
  Left := 100;
  Top := 100;
  Width := 400;
  Height := 300;
  Visible := True;
  Parent := Form; //Any container: form, panel, ...
end;

end;

exports Mymemo_Create;


begin
end.
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

type
 TDLLComponent = procedure(Form:TForm{; ComponentNum: Word}); stdcall;//: Word;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
 Mymemo_Create :TDLLComponent;
 dll_Mymemo :THandle;
 dirplugins :String;
begin
dirplugins:='E:\_Downloads\vk\Mymemo\';
 if Fileexists(dirplugins+'Mymemo.dll') then begin
   dll_Mymemo:= LoadLibrary(PChar(dirplugins+'Mymemo.dll'));
   if dll_Mymemo <> 0 Then begin
    edit1.Text:='load';
   end;
   //@
   Mymemo_Create:= GetProcAddress(dll_Mymemo, 'Mymemo_Create');
  if Assigned(Mymemo_Create) = True then begin
  edit1.Text:='load & create';
   Mymemo_Create(Form1);
  end;
 end;
end;

end.

How to create a component from a DLL?

Upvotes: 0

Views: 625

Answers (2)

FoxWMulder
FoxWMulder

Reputation: 9

I found a solution with DLL connection.Unit FastMM5. In the DPR file, in the Uses add "FastMM5", necessarily in the first place. It is added to the DLL and to the program where the library will be called from. Seems, no crashes, no hanging processes.

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 597941

The error message "cannot assign TFont to a TFont" means that you have an RTTI mismatch, because the TFont class (and other classes) compiled inside the DLL is/are different than the TFont class (and others) compiled inside the host app. As such, when the VCL internally tries to Assign() a TFont object from the DLL to a TFont object in the host app (or vice versa), it can't verify they are instances of the same class type, and so it fails with the error above.

When using components across the DLL boundary, the host app and the DLL MUST be compiled with the same Delphi version, and MUST both be compiled with Runtime Packages enabled, so they can share a single instance of the RTL/VCL frameworks and all of their respective RTTI in memory.

Otherwise, you SHOULD NOT be doing this kind of work with a plain DLL to begin with. You SHOULD be doing it with a BPL Package instead (and thus replace LoadLibrary() with LoadPackage()). A BPL Package is a special kind of DLL with built-in support for the RTL/VCL frameworks. It is designed to solve these kinds of problems for you, whereas you have to manage everything manually in a plain DLL.

Read Delphi's documentation for more details: Working with Packages and Components Index

The fact that your approach "worked" at all in Delphi 2007 was pure luck. What I described is how Delphi has always operated in all versions.


On a side note:

In your "old" code, your TDLLComponent type is declared wrong. It should be:

type
  //TDLLComponent = function(Form:TForm; ComponentNum: Word): Word;
  TDLLComponent = procedure(Form:TForm; ComponentNum: Word);

Upvotes: 0

Related Questions