0

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?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    Are you creating the DLL with D2007 and the host application with D11? A TFom in D2007 won't be recognized as a TForm in D11. You can onmy do that when both DLL and EXE are compiled with same Delphi version _AND_ both use run time packages. – fpiette Apr 16 '23 at 12:55
  • I rebuild and recompile DLL and host application over and over again). It does not help. And the code I've provided seems to work. And highlighting at SynMemo appears, but as soon as I try to edit the text in SynMemo, an error occurs "Access violation ...". But I don't think it's worth considering that the problem is in SynMemo, since I tried to create Memo from a DLL. When creating a Memo, it does not appear on the form and the error "cannot assign TFont to a TFont" appears. – FoxWMulder Apr 16 '23 at 13:46
  • The Internet suggests using BPL. What for if there is a DLL? What for then DLL? Fuck Delphi 11. It's not worth it. – FoxWMulder Apr 16 '23 at 16:49
  • @FoxWMulder it doesn't work for you because it is *not supposed* to work. What you are attempting to do can't (easily) be done with a plain DLL to being with. You need to do it with a BPL instead, from the very beginning. You took the wrong approach, and now you are (finally) seeing the consequences of that decision. It only "worked" before as pure luck, as a fluke. You need to fix your design to do the right thing. – Remy Lebeau Apr 16 '23 at 17:14
  • Try to get rid of `with` statement. Over the years many people complained that `with` statement sometimes leads to unpredictable behavior. Also since you have ported your application from Delphi 2007 to Delphi 11 check path settings within your projects. It is possible that paths in your projects for DLL or your application still point to some units in Delphi 2007 which could lead to difference in RTTI that is compiled into DLL or your application. – SilverWarior Apr 16 '23 at 18:35
  • A package is a dll, but with the necessary tooling to handle complex types in the cross module setting. Plain dlls don't have that support. Do you want your program to work, or not? – David Heffernan Apr 16 '23 at 20:32
  • 1
    This great article explains in much detail what you can do and also what you shouldn't do or even mustn't do when writing DLLs: http://rvelthuis.de/articles/articles-dlls.html – Delphi Coder Apr 16 '23 at 21:22

2 Answers2

0

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);
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1) " TDLLComponent = procedure(Form:TForm; ComponentNum: Word);" - Does not help. 2) Everything is already recompiled in one version. 3) I have never worked with BPL. I can't find an article on my topic. But I don't want to use BPL, if only because I want my "plugins" to be in DLL format. 4) About RTTI, I generally unfortunately do not understand what it is about. It would be easier with examples :) What for in general DLL if they cannot be used. – FoxWMulder Apr 16 '23 at 20:17
  • Worked without problems, now there is a problem. I would like to switch to Delphi 11A, but it seems not fate. – FoxWMulder Apr 16 '23 at 20:23
  • Looks like I've decided. 1) "TDLLComponent = procedure(Form:TForm; ComponentNum: Word);". Here I made a mistake, I understand. Delphi just compiled, so I didn't notice. And added "stdcall;" to this line. 2) To the line "procedure PascalHighlighter_Create(Form:TForm; ComponentNum:Word); stdcall;" also added stdcall; 3) Added ShareMem to Uses. I will try further. Not all errors are immediately detected. – FoxWMulder Apr 16 '23 at 20:33
  • But now the DLL hangs in processes. This is not good. But when you try to unload it yourself, an error occurs "Access violation ..." – FoxWMulder Apr 16 '23 at 20:52
  • @FoxWMulder please read the documentation I linked to. BPLs *are* DLLs, just with better framework support built in to them. There is **no benefit** to using plain DLLs in your design. You are limiting your interface to using compiler-specific types, so you can't create new plugins using non-Delphi compilers, and so forcing your plugins to be plain DLLs is not helping you. Make them as BPLs instead, and then these kind of problems will go away. This is how Delphi is designed and intended to use components in plugin-like scenarios. BPL is Delphi's native library format, not DLL. – Remy Lebeau Apr 16 '23 at 22:39
  • 1
    @FoxWMulder in any case, if you are dead set of using plain DLLs only (and really, don't do that) then enabling ShareMem is not good enough by itself. You MUST turn on use of Runtime Packages in the compiler settings of both host and DLL projects. This will cause both host and DLL to link to RTL/VCL BPLs as external dependencies, thus allowing them to share code, memory objects, and RTTI with each other. Which you need to do in order to share VCL objects over the DLL boundary properly. – Remy Lebeau Apr 16 '23 at 22:45
  • @FoxWMulder Your real problem here is that you have decided on a solution that does not work. You should concentrate on finding a path to a solution that works within the design of the system. – David Heffernan Apr 17 '23 at 08:14
  • 1) Why do DLLs exist if they cannot be used? 2) Okay, let's say I switch to BPL for SOMETHING. I can't find any example of how I should create them. How can I place a component in BPL and then dynamically load it into the program? Can someone give an example please! – FoxWMulder Apr 19 '23 at 03:33
  • @FoxWMulder 1) DLL tech predates BPL tech. DLLs are still useful, but NOT for your specific situation. BPLs ARE DLLs, just with MORE functionality builtin *specifically designed* for your situation. 2-3) Did you read the documentation I linked to in my answer? – Remy Lebeau Apr 19 '23 at 04:53
  • @RemyLebeau Did you mean https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Working_with_Packages_and_Components_Index? I can't figure it out without an example, unfortunately. I could probably add a component to the BPL, load the BPL, but when calling the procedure from the BPL, I get an "access violation" error – FoxWMulder Apr 19 '23 at 09:28
  • Now it turned out to create a SynPasSyn component from BPL, call a function so that the highlight in SynEdit works. But when I try to edit the text, I get an "access violation" error . – FoxWMulder Apr 19 '23 at 10:36
  • @FoxWMulder please edit your question to show your updated code – Remy Lebeau Apr 19 '23 at 14:33
0

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.