Link to home
Start Free TrialLog in
Avatar of piney
piney

asked on

how to make a plugin using .dll in delphi

I want to make a component plugin using .dll in delphi,
but it seem that very hard to do, thank you to answer my
question, and sorry for my poor english.
Avatar of rwilson032697
rwilson032697

The key to making plugins is that each DLL has exactly the same interface. eg:

procedure DoSomething(AnArg : SomeType);
procedure DoSomethingElse(AnArg : SomeType);

Each DLL implements this very same set of procedures and functions. Then when you want to plug a DLL in you just use LoadLibrary and GetProcAddress to load in the DLL and then get the addresses of the functions within it eg:

Handle := LoadLibrary('ThePlugIn.dll');
TheDoSomethingProc = GetProcAddress(Handle, 'DoSomething');
TheDoSomethingElseProc = GetProcAddress(Handle, 'DoSomethingElse');

to call it do this:

@TheDoSomethingProc(AnInstanceOfSomeType);

You can set up a structure that stores all of these. Then with an array or similar of these you can manage multiple plugins.

Cheers;

Raymond.
Avatar of piney

ASKER

I want to Declare a NEW ControlClass in a .DLL, then my main application can plugin this ControlClass, and create some instance of this ControlClass, and I can control the controls what I created...
Thank you!
Sounds like you want to create a COM object. Have a read in the Delphi help about COM objects and object factories...

Cheers,

Raymond.
Avatar of piney

ASKER

But I don't know how to designing and using a COM object, so I
won't to using a COM object to design my Plugins, in other way, using the packages can design plugin( using 'LoadPackage', 'RegisterClass', ... ), but I only want to using a simple DLL to do it, can you tell me how to do it...
Thank you!!!

Avatar of piney

ASKER

But I don't know how to designing and using a COM object, so I
won't to using a COM object to design my Plugins, in other way, using the packages can design plugin( using 'LoadPackage', 'RegisterClass', ... ), but I only want to using a simple DLL to do it, can you tell me how to do it...
Thank you!!!

In that case the approach I outlined in my previous answer is the way to do it. Instead of LoadPackage you use LoadLibrary (in reality a package is just a DLL anyway...) The DLL itself can register the class...

You can have procedures in the DLL that create and return instances of your new control class and away you go eg: in the DLL:

Function NewControl({some arguments for its creation}):TNewControlClass;

In the App:

ANewControl : ControlClassBase;

ANewControl := (@NewControl)({some arguments for its creation});

ANewControl.DoYourStuff;

This should give you a good picture of how to do it.

Cheers,

Raymond.

Avatar of piney

ASKER

I did it with except raise when I type NewControl.Parent := Form1;
It seem that the NewControl's Parent can't assign, how to fix it?
Thank you!!!


My Program:

// In the DLL
type
  TMyX = class( TBitBtn )
  public
    constructor Create( AOwner: TComponent ); override;
    destructor Destroy; override;
  end;

constructor TMyX.Create( AOwner: TComponent );
begin
  inherited Create( AOwner );
end;

destructor TMyX.Destroy;
begin
  inherited Destroy;
end;

function NewControl: TMyX;
begin
  Result := TMyX.Create( nil );
end;

exports
  NewControl;
end.

// In the app
function NewControl: TControl; external 'PLUGIN.DLL';

procedure TForm1.Button1Click(Sender: TObject);
var
  ANewControl: TControl;
begin
  ANewControl := @NewControl;
  ANewControl.Parent := Form1;  // errors create in this line !!!
end;

Avatar of piney

ASKER

I did it with except raise when I type NewControl.Parent := Form1;
It seem that the NewControl's Parent can't assign, how to fix it?
Thank you!!!


My Program:

// In the DLL
type
  TMyX = class( TBitBtn )
  public
    constructor Create( AOwner: TComponent ); override;
    destructor Destroy; override;
  end;

constructor TMyX.Create( AOwner: TComponent );
begin
  inherited Create( AOwner );
end;

destructor TMyX.Destroy;
begin
  inherited Destroy;
end;

function NewControl: TMyX;
begin
  Result := TMyX.Create( nil );
end;

exports
  NewControl;
end.

// In the app
function NewControl: TControl; external 'PLUGIN.DLL';

procedure TForm1.Button1Click(Sender: TObject);
var
  ANewControl: TControl;
begin
  ANewControl := @NewControl;
  ANewControl.Parent := Form1;  // errors create in this line !!!
end;

ASKER CERTIFIED SOLUTION
Avatar of rwilson032697
rwilson032697

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of piney

ASKER

You procedure

function NewControl: TMyX(Owner : TComponent);
begin
  Result := TMyX.Create( Owner );
end;

could not be compile, and I write it like this:

function NewControl( AOwner: TComponent ): TMyX;
begin
  Result := TMyX.Create( AOwner );
end;


It seem that an control plugined, but when I assign it's parent,
an error "cannot assign a TFont to a TFont" created...
My plugin could not visible, how to fix it ???

oh, to make a plugin is so difficulty... :-<

and thank you to guide me to fix it!!!
Can you post a code sample that causes the error?

Raymond.
Avatar of piney

ASKER

Here is my codes

// My dll:

library Plugin;

uses
  SysUtils, Classes, Buttons;

type
  TMyX = class( TBitBtn )
  public
    constructor Create( AOwner: TComponent ); override;
    destructor Destroy; override;
  end;

constructor TMyX.Create( AOwner: TComponent );
begin
  inherited Create( AOwner );
end;

destructor TMyX.Destroy;
begin
  inherited Destroy;
end;

function NewControl( AOwner: TComponent ): TMyX;
begin
  Result := TMyX.Create( AOwner );
end;

exports
  NewControl;
end.

// My app:

unit Main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

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

var
  Form1: TForm1;

implementation

{$R *.DFM}

function NewControl( AOwner: TComponent ): TControl; external 'PLUGIN.DLL';

procedure TForm1.Button1Click(Sender: TObject);
var
  AControl: TControl;
begin
  AControl := NewControl( Self );
  AControl.Parent := Form1;
end;

end.
I haven't had a chanve to try this out yet, but from reading your code I think you need to register the the class in the DLL (best put in the automatic initialisation for the DLL.

RegisterClasses([TMyX]);

Let me know if it works...

Cheers,

Raymond.

Avatar of piney

ASKER

No, the same error raise too!!!
I don't understand what is happening here - you do nothing with fonts. The bitbtn will have a font property which may be where it is coming from - though how assigning its parent causes it I do not know. If I get time tomorrow I will have a go putting your code into Delphi...

Cheers,

Raymond.
Avatar of piney

ASKER

Thank you Raymond... :)
Hmmm....

I have reproduced the problem - but do not understand why it occurs unless there is some resource management contention problem between the app and the dll. I have tried a few different approaches but all result in the exception.

You might want to take a look at this question (currently still open) which has a detailed discussion on this topic...

https://www.experts-exchange.com/topics/comp/lang/delphi/Q.10106878

Cheers,

Raymond.

Avatar of piney

ASKER

In my dll:

procedure NewControl( hParent, OwnerHandle: THandle ); stdcall; export;
begin
  AMyX:= TMyX.CreateParented( hParent );
  AMyX.Left:= 0;
  AMyX.Top:= 0;
  Windows.SetParent( AMyX.Handle, OwnerHandle);
  AMyX.Show;
end;

exports
  NewControl;

In my app:

var
  DllHandle: THandle;
  NewControl: procedure( hParent, OwnerHandle: THandle ); stdcall;
begin
  DllHandle := LoadLibrary('PLUGIN.dll');
  if DllHandle <> 0 then
  begin
    @NewControl := GetProcAddress(DllHandle, 'NewControl');
    NewControl( Handle, handle );
  end;
  Caption := IntToStr( ControlCount );
End;

  it doesn't works, I thing is was "Windows.SetParent" raise errors...

You could also try adding a procedure to the DLL that takes the TApplication Object from the application and assigns it to the application object in the dll (remember to preserve the one in the DLL as you will need to restore it before closing down...) - though this appears more relevant to making MDI forms work OK from a DLL.

Cheers,

Raymond.
Avatar of piney

ASKER

But how to do it? Can you send me to source-code?
in the dll:

var
  SaveApp : TApplication;

procedure SetApplication(App : TApplication); stdcall;

begin
  SaveApp := Application;
  Application := App;
end;

Declare thisin the usual fashion in the exe and call it like this in your exe:

SetApplication(Application);

Cheers,

Raymond.

Avatar of piney

ASKER

In my App:

var
  DllHandle: THandle;
  NewControl: procedure( hParent, OwnerHandle: THandle ); stdcall;
  SetApplication: procedure( App: TApplication ); stdcall;
  RestoreApplication: procedure; stdcall;
begin
  DllHandle := LoadLibrary('C:\DESIGN\PLUGINS\PLUGIN.dll');
  if DllHandle <> 0 then
  begin
    @SetApplication := GetProcAddress(DllHandle, 'SetApplication');
    @RestoreApplication := GetProcAddress(DllHandle, 'RestoreApplication');
    @NewControl := GetProcAddress(DllHandle, 'NewControl');
    SetApplication( Application );
    NewControl( Handle, Handle );
//    RestoreApplication;
//    FreeLibrary( DllHandle );
  end;
End;

In my Dll:

var
  SaveApp: TApplication;

constructor TMyX.Create( AOwner: TComponent );
begin
  inherited Create( AOwner );
end;

destructor TMyX.Destroy;
begin
  inherited Destroy;
end;

procedure NewControl( hParent, OwnerHandle: THandle ); stdcall; export;
var
  AMyX: TWinControl;
begin
  AMyX:= TMyX.CreateParented( hParent );
  AMyX.Left:= 0;
  AMyX.Top:= 0;
  Windows.SetParent( AMyX.Handle, OwnerHandle );
  AMyX.Show;
end;

procedure SetApplication( App: TApplication ); stdcall;
begin
  SaveApp := Application;
  Application := App;
end;

procedure RestoreApplication; stdcall;
begin
  Application := SaveApp;
end;

exports
  SetApplication,
  NewControl,
  RestoreApplication;

end.

It cannot work, too!!!
Oh, is it very difficult in plugin programming???????
What goes wrong?
Avatar of piney

ASKER

The control isn't created, and after the app close, there is a  messagebox( "Runtime error 216 at 00002C10") created, It seem that we can't make a plugin by this way.
Piney,

I have had a play with your modified source without a lot of luck, the changes are below... I tracked it down to the fact it was trying to use the font of the parent when its parent property was assigned. This seemed to cause problems so I tried to change the ParentFont property to false, but this would not compile which is weird as it is defined!

I have run out of ideas here - perhaps it would be better to post this as a seperate question to attract a wider audience of experts...

library Plugin;

       uses
         SysUtils,
  Classes,
  Buttons, stdctrls, controls, forms, windows;

       type
         TMyX = class( TBitBtn )
         public
           constructor Create( AOwner: TComponent ); override;
           destructor Destroy; override;
         end;

       var
         SaveApp: TApplication;

       constructor TMyX.Create( AOwner: TComponent );
       begin
         inherited Create( AOwner );
       end;

       destructor TMyX.Destroy;
       begin
         inherited Destroy;
       end;

       function NewControl( Owner : TWinControl; hParentHandle, OwnerHandle: THandle ):TWinControl; stdcall; export;
       var
         AMyX: TWinControl;
       begin
//         AMyX:= TMyX.CreateParented( hParentHandle );
         AMyX := TMyX.Create(Owner);
//         AMyX.Parent := Owner;
         AMyX.Left:= 0;
         AMyX.Top:= 0;
         Windows.SetParent( AMyX.Handle, OwnerHandle );
         AMyX.Show;

         result := AMyX;
       end;

       procedure SetApplication( App: TApplication ); stdcall;
       begin
         SaveApp := Application;
         Application := App;
       end;

       procedure RestoreApplication; stdcall;
       begin
         Application := SaveApp;
       end;

       exports
         SetApplication,
         NewControl,
         RestoreApplication;

       end.






       // My app:

       unit unit1;

       interface

       uses
         Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
         StdCtrls;

       type
         TForm1 = class(TForm)
    Button1: TButton;

           procedure Button1Click(Sender: TObject);
         private
           { Private declarations }
         public
           { Public declarations }
         end;

       var
         Form1: TForm1;

       implementation

       {$R *.DFM}


       function NewControl( AOwner: TComponent ): TWinControl; external 'PLUGIN.DLL';

       procedure TForm1.Button1Click(Sender: TObject);
       var
         AControl: TWinControl;
         DllHandle: THandle;
         NewControl: function( Owner : TWinControl; hParentHandle, OwnerHandle: THandle ):TWinControl; stdcall;
         SetApplication: procedure( App: TApplication ); stdcall;
         RestoreApplication: procedure; stdcall;
       begin
         DllHandle := LoadLibrary('C:\dev\PLUGIN\PLUGIN.dll');
         if DllHandle <> 0 then
         begin
           @SetApplication := GetProcAddress(DllHandle, 'SetApplication');
           @RestoreApplication := GetProcAddress(DllHandle, 'RestoreApplication');
           @NewControl := GetProcAddress(DllHandle, 'NewControl');
           SetApplication( Application );
           AControl := NewControl( Self, Handle, Handle );
           windows.Setparent(Acontrol.handle, handle);
//           RestoreApplication;
//           FreeLibrary( DllHandle );
         end;
       End;
end.
Avatar of piney

ASKER

Rwilson

Thank you to answer my question and took a long time to fix my program!