LEEJG
asked on
URGENT: Creating package containing objects, loading it and using the object...
First time using packages and I can't get objects passed to my apps. Offering 500 points for VERY quick response:
Here is my code...
"User.dpk" package that contains "untUser.pas" + CREATE_PACKAGE defined:
TObject_User = class (TPersistent)
procedure Execute(const sMode: String);
end;
{$ifdef CREATE_PACKAGE}
initialization
RegisterClass(TObject_User );
finalization
UnRegisterClass(TObject_Us er);
{$endif}
end.
========================== ========== ====
Calling application that has:
uses untUser;
type TUserClass = class of TObject_Group_IBS;
procedure TForm1.Button1Click(Sender : TObject);
var
hPackage: HModule;
oClass: TPersistentClass;
begin
hPackage:=LoadPackage('Use r.bpl');
if hPackage=0 then raise Exception.Create('Cannot load pacakge');
try
oClass:=GetClass('TObject_ User');
if not assigned(oClass) then raise Exception.Create('Cannot find class');
with TUserClass(oClass).Create( ) do begin
try
Execute('Add');
finally
Free;
end;
end;
finally
UnloadPackage(hPackage);
end;
end;
Help help help!
Here is my code...
"User.dpk" package that contains "untUser.pas" + CREATE_PACKAGE defined:
TObject_User = class (TPersistent)
procedure Execute(const sMode: String);
end;
{$ifdef CREATE_PACKAGE}
initialization
RegisterClass(TObject_User
finalization
UnRegisterClass(TObject_Us
{$endif}
end.
==========================
Calling application that has:
uses untUser;
type TUserClass = class of TObject_Group_IBS;
procedure TForm1.Button1Click(Sender
var
hPackage: HModule;
oClass: TPersistentClass;
begin
hPackage:=LoadPackage('Use
if hPackage=0 then raise Exception.Create('Cannot load pacakge');
try
oClass:=GetClass('TObject_
if not assigned(oClass) then raise Exception.Create('Cannot find class');
with TUserClass(oClass).Create(
try
Execute('Add');
finally
Free;
end;
end;
finally
UnloadPackage(hPackage);
end;
end;
Help help help!
ASKER
So are you saying I need to base my object on something like a TComponent? I did try this and couldn't get it to work.
I have found lots of examples on the net but they all seem to be based on showing you how to use a TForm in a package and just use things like ShowModal (a standard method of TForm). I need to access properties and methods of MY class (like Execute) and not the standard ones of a TForm (like Top, ShowModal, etc).
I have found lots of examples on the net but they all seem to be based on showing you how to use a TForm in a package and just use things like ShowModal (a standard method of TForm). I need to access properties and methods of MY class (like Execute) and not the standard ones of a TForm (like Top, ShowModal, etc).
no, problem is in fact, that RegisterClass and FindClass declared in classes.pas mantains private list of registered classes in unit and if you don't use runtime packages, then dynamically loaded library will have their own instance of classes.pas and respectively, registered classes list. you can see registered classes in package when calling IN package, but exe loaded that package will see only registered classes within that exe.
that's why you must use RUNTIME package, with classes.pas unit contained, for both - EXE and BPL. To achieve this:
- in exe project Options dialog in packages tab, check Build with runtime packages and type VCLxx (where xx is version of delfi. for Delphi 7 you must write without version - simply VCL) in the packages edit box (you don't need, at least at the moment, any other package)
- in bpl check whether your bpl haves vclxx.dcp in Requires treeview section, if not - add it with Add button.
build both projects and you must get all to work.
wbr, mo.
that's why you must use RUNTIME package, with classes.pas unit contained, for both - EXE and BPL. To achieve this:
- in exe project Options dialog in packages tab, check Build with runtime packages and type VCLxx (where xx is version of delfi. for Delphi 7 you must write without version - simply VCL) in the packages edit box (you don't need, at least at the moment, any other package)
- in bpl check whether your bpl haves vclxx.dcp in Requires treeview section, if not - add it with Add button.
build both projects and you must get all to work.
wbr, mo.
You may use TObject.MethodAddress procedure to obtain address of published method. So you need make all your methods you want to use outside the packgae published.
ok, that was about why you can't find class (if that was a problem too :) my missunderstood, likely :) but anyway you must use runtime packages to register classes..
I figured out two methods to call methods which passes parameters from dynamically loaded package:
1) by implementing interfaces in package classes and registering them (classes resp.) with RegisterClass:
unit MyInterface; // compiles in both package and exe
interface
type
IMyInterface = interface
['{E648502D-F035-41B0-B53A -FCC3A02DF 3A6}']
procedure Execute(const aMode: string); stdcall;
end;
implementation
end.
unit Component2; // in package
type
TComponent2 = class(TComponent, IMyInterface)
private
procedure Execute(const aMode: string); stdcall;
end;
implementation
procedure TComponent2.Execute(const aMode: string);
begin ...
end;
initialization
RegisterClass(TComponent2) ;
finalization
UnRegisterClass(TComponent 2);
end.
and in exe:
...
implementation
uses MyInterface;
procedure TForm1.btnFindClassClick(S ender: TObject);
var
hPackage: HModule;
IMI: IMyInterface;
begin
hPackage := LoadPackage('User.bpl');
if hPackage = 0 then
raise Exception.Create('Cannot load pacakge');
try
// findclass will rise exception we need
with TComponentClass(FindClass( 'TComponen t2')).Crea te(nil) do begin
try
if GetInterface(IMyInterface, IMI) then
IMI.Execute('Add');
finally
// ensure reference count is set to 0
IMI := nil;
Free;
end;
end;
finally
UnloadPackage(hPackage);
end;
end;
2) by using standard DLL function export routine (exports keyword) and in this case you don't need compile with runtime packages (but anyway it is better to compile if units overlaps)
unti Component2; // in package
uses..
TObject2 = class(TObject)
procedure Execute(const aMode: string);
end;
procedure Execute(const aMode: string); stdcall;
implementation
procedure Execute(const aMode: string); stdcall;
begin
with TObject2.Create do
try Execute(aMode);
finally free; end;
end;
procedure TObject2.Execute(const aMode: string);
begin ..
end;
exports
Execute;
end.
and calling from exe:
...
type TExecuteProc = procedure(const aMode: string); stdcall;
procedure TForm1.btnExportClick(Send er: TObject);
var
hPackage: HModule;
ep: TExecuteProc;
begin
hPackage := LoadPackage('User.bpl');
if hPackage = 0 then
raise Exception.Create('Cannot load pacakge');
try
@ep := GetProcAddress(hPackage, 'Execute');
if @ep = nil then
raise Exception.Create('Package doesn''t exports Execute procedure');
ep('Add');
finally
UnloadPackage(hPackage);
end;
end;
I have placed full example project sources on:
http://www.grava.lv/files/pro/load_d5.zip
wbr, mo.
I figured out two methods to call methods which passes parameters from dynamically loaded package:
1) by implementing interfaces in package classes and registering them (classes resp.) with RegisterClass:
unit MyInterface; // compiles in both package and exe
interface
type
IMyInterface = interface
['{E648502D-F035-41B0-B53A
procedure Execute(const aMode: string); stdcall;
end;
implementation
end.
unit Component2; // in package
type
TComponent2 = class(TComponent, IMyInterface)
private
procedure Execute(const aMode: string); stdcall;
end;
implementation
procedure TComponent2.Execute(const aMode: string);
begin ...
end;
initialization
RegisterClass(TComponent2)
finalization
UnRegisterClass(TComponent
end.
and in exe:
...
implementation
uses MyInterface;
procedure TForm1.btnFindClassClick(S
var
hPackage: HModule;
IMI: IMyInterface;
begin
hPackage := LoadPackage('User.bpl');
if hPackage = 0 then
raise Exception.Create('Cannot load pacakge');
try
// findclass will rise exception we need
with TComponentClass(FindClass(
try
if GetInterface(IMyInterface,
IMI.Execute('Add');
finally
// ensure reference count is set to 0
IMI := nil;
Free;
end;
end;
finally
UnloadPackage(hPackage);
end;
end;
2) by using standard DLL function export routine (exports keyword) and in this case you don't need compile with runtime packages (but anyway it is better to compile if units overlaps)
unti Component2; // in package
uses..
TObject2 = class(TObject)
procedure Execute(const aMode: string);
end;
procedure Execute(const aMode: string); stdcall;
implementation
procedure Execute(const aMode: string); stdcall;
begin
with TObject2.Create do
try Execute(aMode);
finally free; end;
end;
procedure TObject2.Execute(const aMode: string);
begin ..
end;
exports
Execute;
end.
and calling from exe:
...
type TExecuteProc = procedure(const aMode: string); stdcall;
procedure TForm1.btnExportClick(Send
var
hPackage: HModule;
ep: TExecuteProc;
begin
hPackage := LoadPackage('User.bpl');
if hPackage = 0 then
raise Exception.Create('Cannot load pacakge');
try
@ep := GetProcAddress(hPackage, 'Execute');
if @ep = nil then
raise Exception.Create('Package doesn''t exports Execute procedure');
ep('Add');
finally
UnloadPackage(hPackage);
end;
end;
I have placed full example project sources on:
http://www.grava.lv/files/pro/load_d5.zip
wbr, mo.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thanks for all your help everyone, Not as easy as I thought it would be !! ... But anyway it now works great.
I am giving the points to mocarts.
I am giving the points to mocarts.
https://www.experts-exchange.com/questions/20548192/Registering-a-TFrame-class.html
wbr, mo.