Solved

how to make a plugin using .dll in delphi

Posted on 1998-11-30
26
532 Views
Last Modified: 2013-11-18
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.
0
Comment
Question by:piney
  • 14
  • 12
26 Comments
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1348676
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.
0
 

Author Comment

by:piney
ID: 1348677
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!
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1348678
Sounds like you want to create a COM object. Have a read in the Delphi help about COM objects and object factories...

Cheers,

Raymond.
0
 

Author Comment

by:piney
ID: 1348679
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!!!

0
 

Author Comment

by:piney
ID: 1348680
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!!!

0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1348681
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.

0
 

Author Comment

by:piney
ID: 1348682
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;

0
 

Author Comment

by:piney
ID: 1348683
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;

0
 
LVL 12

Accepted Solution

by:
rwilson032697 earned 200 total points
ID: 1348684
The problem is in your NewControl and Button click procedures: Do this:

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

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

This should do the trick!

Cheers,

Raymond.

PS: You don't have to reject an answer to continue discussing it with the expert who answered it - just add another comment!
0
 

Author Comment

by:piney
ID: 1348685
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!!!
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1348686
Can you post a code sample that causes the error?

Raymond.
0
 

Author Comment

by:piney
ID: 1348687
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.
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1348688
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.

0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 

Author Comment

by:piney
ID: 1348689
No, the same error raise too!!!
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1348690
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.
0
 

Author Comment

by:piney
ID: 1348691
Thank you Raymond... :)
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1348692
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...

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

Cheers,

Raymond.

0
 

Author Comment

by:piney
ID: 1348693
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...

0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1348694
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.
0
 

Author Comment

by:piney
ID: 1348695
But how to do it? Can you send me to source-code?
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1348696
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.

0
 

Author Comment

by:piney
ID: 1348697
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???????
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1348698
What goes wrong?
0
 

Author Comment

by:piney
ID: 1348699
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.
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1348700
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.
0
 

Author Comment

by:piney
ID: 1348701
Rwilson

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

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
The purpose of this video is to demonstrate how to set up the WordPress backend so that each page automatically generates a Mailchimp signup form in the sidebar. This will be demonstrated using a Windows 8 PC. Tools Used are Photoshop, Awesome…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now