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.
but it seem that very hard to do, thank you to answer my
question, and sorry for my poor english.
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!
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.
Cheers,
Raymond.
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!!!
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!!!
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!!!
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}):TNewControlClas s;
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.
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}):TNewControlClas
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.
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;
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
var
ANewControl: TControl;
begin
ANewControl := @NewControl;
ANewControl.Parent := Form1; // errors create in this line !!!
end;
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;
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
var
ANewControl: TControl;
begin
ANewControl := @NewControl;
ANewControl.Parent := Form1; // errors create in this line !!!
end;
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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!!!
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.
Raymond.
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.
// 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
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.
RegisterClasses([TMyX]);
Let me know if it works...
Cheers,
Raymond.
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.
Cheers,
Raymond.
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.
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.
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...
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.
Cheers,
Raymond.
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.
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.
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\PLU GINS\PLUGI N.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???????
var
DllHandle: THandle;
NewControl: procedure( hParent, OwnerHandle: THandle ); stdcall;
SetApplication: procedure( App: TApplication ); stdcall;
RestoreApplication: procedure; stdcall;
begin
DllHandle := LoadLibrary('C:\DESIGN\PLU
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?
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.dl l');
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.
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
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
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
// RestoreApplication;
// FreeLibrary( DllHandle );
end;
End;
end.
ASKER
Rwilson
Thank you to answer my question and took a long time to fix my program!
Thank you to answer my question and took a long time to fix my program!
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(AnInst
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.