1

I am trying to create a TButton object in DLL to show it up in EXE Main Form.Here is My Code:

DLL:

library dlltest1;

{$mode objfpc}{$H+}

uses
  Classes,sysutils,Forms,Interfaces,StdCtrls,Windows,Dialogs
  { you can add units after this };
function test(hand:TForm):HWND;

var
  a:TButton;
begin
  a:=TButton.Create(hand);
  a.Show;
  a.Caption:='a';
  result:=a.Handle;
end;

exports test;
begin
end.  

EXE:

  procedure test(hand:HWND);  external 'dlltest1.dll';
type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    TIPropertyGrid1: TTIPropertyGrid;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }
  type TComp=function(Hand:HWND):HWND;
var
  comps:array of TComp;

procedure TForm1.FormCreate(Sender: TObject);
begin
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  a:HWND;
begin
SetLength(comps,1);
a:=LoadLibrary('dlltest1.dll');
comps[0]:=TComp(GetProcAddress(a,'test'));
(FindControl(comps[0](Form1.Handle)) as TButton).Caption:='A'; 
end;

It creates button succesfuly with command comps[0](Form1.Handle) but when i try to execute this (FindControl(comps[0](Form1.Handle)) as TButton).Caption:='A'; ,it says INVALID TYPE CAST.I checked the class name in main exe.ClassName was TButton.I am compiling both projects under Lazarus IDE for Windows x86_64.I also tried to use RTTI TPropertyEditor Grid.When i assign it like:TIPropertyEditor1.TIObject:=FindControl(comps[0](Form1.Handle)) TIPropertyEditor1 acts like it as TButton as normal.But I can't figure out why 'as TButton ' causes Invalid Type Cast.Is there any solution for my problem?

HolyLilly
  • 43
  • 6
  • 1
    Type identity doesn't work as you'd expect across modules. Use packages which are designed for this purpose. Assuming Lazarus has package support. – David Heffernan Jul 10 '18 at 12:23
  • Lazarus has package support,If you have any suggestions to tell me about ,I would be very happy – HolyLilly Jul 10 '18 at 12:33
  • Surely there is documentation – David Heffernan Jul 10 '18 at 12:37
  • TIObject can sense it is TButton and it can handle it without exception.So that means Type identity works across modules.But i am looking for answer about why (as TButton) causes "Invalid Type Cast" exception and how to solve it. – HolyLilly Jul 10 '18 at 12:39
  • I also tried to run this code under Delphi 10.2 Tokyo,It is doing nothing.Not creating any Buttons on Form. – HolyLilly Jul 10 '18 at 12:42
  • Nope, type identity really doesn't work as you'd hope over module boundaries, unless you use runtime packages. At least in Delphi. See countless questions on this topic. – David Heffernan Jul 10 '18 at 12:44
  • Then why it is working with RTTI? It can read and write to control over module."TIPropertyGrid1.TIObject:=FindControl(comps[0](Form1.Handle));".That code works quite well. – HolyLilly Jul 10 '18 at 12:48
  • Nothing there depends on type identity does it? It's the `as` checked cast that is failing. I'm not sure this is going to be productive because I feel like you don't want to open your mind to your beliefs being wrong. Also, I'm not a lazarus expert. – David Heffernan Jul 10 '18 at 12:52
  • RTTI knows that it is TButton which means there is a type identity.try not to comment if you are not going to be productive. – HolyLilly Jul 10 '18 at 13:02
  • And that is not a belief,i got an exe running. – HolyLilly Jul 10 '18 at 13:03
  • I'm glad you've solved the problem. We'll done. – David Heffernan Jul 10 '18 at 13:08
  • You can't understand right? Whatever... – HolyLilly Jul 10 '18 at 13:10
  • Problem is not solved.Next time try to read posts. – HolyLilly Jul 10 '18 at 13:13
  • You said the issue with type identity was solved. The error cause TButton in one module is different from TButton in another module. Unless you use packages. That's how it is in Delphi and knowing how you'd implement type identity it is surely the same in fpc. But you say different. – David Heffernan Jul 10 '18 at 13:14
  • I did not used any word like "SOLVED".I need assign events etc.. Still need to use (as)(is) or something else. which causes exception.Anyone that ones how to assign events using RTTI TIObject,that would be helpfull unlike yours. – HolyLilly Jul 10 '18 at 13:18
  • You can't use is or as across module boundaries for the reasons I said before. Unless you switch to packages. Type identity doesn't work the way you expect across module boundaries. Or do you know better? – David Heffernan Jul 10 '18 at 13:19
  • You can easily reproduce error.Why won't you check for that with both lazarus and delphi? – HolyLilly Jul 10 '18 at 13:19
  • If it is not working between modules how TIObject knows remote object is TButton? – HolyLilly Jul 10 '18 at 13:23
  • It doesn't. In that code you don't check type identity. If you are so sure that this thing really is a `TButton`, ask yourself why `as` disagrees with you? You think you are right and it is wrong? We have gone round in circles. At some point you will realise that your beliefs are wrong. – David Heffernan Jul 10 '18 at 13:27
  • It does.TIObject can tell me it is TButton... – HolyLilly Jul 10 '18 at 13:30
  • It can call the class name method to find that out. Ask yourself why `as` thinks it isn't a `TButton`. – David Heffernan Jul 10 '18 at 13:35
  • you say packages doing some magic,and you dont know what it is,and also you say they can't do type identity which they commonly do,most importantly you say i need to use packages ,which i already use... – HolyLilly Jul 10 '18 at 13:36
  • You aren't using packages. You are using a DLL. How are you going to learn anything if you can't open your mind to the fact you might be wrong? – David Heffernan Jul 10 '18 at 13:37
  • do you know what package means? In delphi you have an file extensions like *.dpk,In lazarus it is *.lpk.I am using RTTI package.Which is a package.which comes in with *.lpi. – HolyLilly Jul 10 '18 at 13:42
  • Yes I know what a package is. In the question it says DLL and the code starts `library`. Has it occurred to you that I might have a little experience in this area? – David Heffernan Jul 10 '18 at 13:43
  • I am not saying "THIS CODE HAD TO WORK AAAAARGH".I am looking for GOOD kind of explanation why It can't work,why rtti works and a good solution for event link. – HolyLilly Jul 10 '18 at 13:45
  • In main form i am using RTTI which is package that can handles DLL objects in EXE Form. – HolyLilly Jul 10 '18 at 13:46
  • Well, are you using a packages or a DLL? Because the code in the question would work with a package, but not a DLL. The code in the question looks like a DLL. But you claim that you are using runtime packages. And if it is a DLL then the issue is type identity. Perhaps you should ask me what I mean by type identity if you don't know what I mean, rather than stating "that means type identity works across modules". Do you want to learn or not? – David Heffernan Jul 10 '18 at 13:48
  • Main Form Project includes a package which named with "RTTI",DLL Project includes just a code that creates TButton using TForm.Handle.As you see in second code.When i click the button(Which is not generated by DLL).It generates a button which summoned by DLL.But I need to change it's caption,event etc... I tried to use (as)(is) but they didn't worked at all in Lazarus.(Invalid type cast).In delphi it just did nothing(Not even created a Button).In lazarus,I added TIPropertyGrid component to my MainForm and set TIObject to DLL Button.It was just ok but i couldn't set it's event activity. – HolyLilly Jul 10 '18 at 13:54
  • If it is not still clear i can tell you about what i am trying to do with this.Hope you will help. – HolyLilly Jul 10 '18 at 13:57
  • So the button is created in a DLL and the issue is type identity. `TButton` in the DLL is different from `TButton` in other modules. Type identity is implemented by pointer comparison. When you do something like `obj.ClassType` that returns a pointer to the type. If those pointers are the same, the types are identical. Each module has its own copies of types. Hence the issue with type identity. Packages deal with this by resolving types at runtime dynamically. I'm not going to say any more. This has been rather bruising for me. Your attitude has been unproductive. Websearch will help you. – David Heffernan Jul 10 '18 at 13:59
  • Please look back over these comments and reflect. "try not to comment if you are not going to be productive", "next time try to read posts". – David Heffernan Jul 10 '18 at 14:02
  • At least you read a little somehow.that surprised me. Websearch would be better than your aggressive comments – HolyLilly Jul 10 '18 at 14:21
  • No, the aggressive comments that I quoted were your comments! – David Heffernan Jul 10 '18 at 14:24
  • They was not aggressive comments,you are taking them as aggressive,replying to me with aggressive words.That is embarrassing... – HolyLilly Jul 10 '18 at 14:42
  • I don't agree. Anyway, I think I answered your question. – David Heffernan Jul 10 '18 at 14:44
  • You answered about half.I think i will come up with more spesific answer. – HolyLilly Jul 10 '18 at 14:57
  • @HolyLilly There are two articles in the Lazarus Wiki that could to assist you: [Creating LCL Control From Libraries](http://wiki.freepascal.org/Creating_LCL_Control_From_Libraries) and [Form in DLL](http://wiki.freepascal.org/Form_in_DLL) – Abelisto Jul 10 '18 at 20:07
  • @Abelisto Thumbs Up! That worked for me,even "as" and "is" working with it!.Thank you :) – HolyLilly Jul 10 '18 at 20:52
  • You are welcome :) @DavidHeffernan And somebody should to clean all unnecessary previous flood. – Abelisto Jul 10 '18 at 21:07
  • @Abelisto Only a diamond mod can clean comments, you can flag the question to request that – David Heffernan Jul 11 '18 at 07:46

2 Answers2

3

Packages (as in BPL packages) are worked on in trunk, but not yet operational. Designtime components are compiled into the IDE binary (which also has advantages like Lazarus' much faster start)

Without those using the normal .so/dlls, besides the identity problem, both the DLL and the main program have full copies of the RTL and its state, which includes the memory manager, VMTs, RTTI, localization etc etc. Using managed types or any scenario where one module allocates and the other deallocates is dangerous in such case. The AS case uses classtype information which is also duplicate per module.

See also http://wiki.freepascal.org/packages for a short treatise.

P.s. David is mostly right and the state for Lazarus is roughly the same as for Delphi, except that there are no working packages yet, nor a shared memory manager concept to work around that.

Marco van de Voort
  • 25,628
  • 5
  • 56
  • 89
-1

It is right decision to send reference to Form instance to button creation call, as only forn is die it will call destructor of all child component.

You should return from test functon not Handle but TButton instannce, difference between HWND and TButton. Delphi wrap OS GUI APIs inside TButton, TForm, any TWinControl instances. On low level OS use HWND as references to OS low level objects.

You should not worry about memory leaks cause of you send Form instance to TButton.Create constructor.

function test(Owner: TForm): TButton;
begin
  Result := TButton.Create(Owner);
  Result.Show;
  Result.Caption:='a';
end;
Pavel Minenkov
  • 368
  • 2
  • 9