• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 396
  • Last Modified:

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_User);
{$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('User.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!
0
LEEJG
Asked:
LEEJG
  • 4
  • 2
1 Solution
 
mocartsCommented:
you must use runtime package which contains classes.pas unit (normally vcl.dcp) in both exe and bpl.
http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20548192.html

wbr, mo.
0
 
LEEJGAuthor Commented:
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).
0
 
mocartsCommented:
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.
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
stepashkaCommented:
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.
0
 
mocartsCommented:
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-FCC3A02DF3A6}']
      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(TComponent2);
end.

and in exe:
...
implementation
uses MyInterface;

procedure TForm1.btnFindClassClick(Sender: 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('TComponent2')).Create(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(Sender: 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.
0
 
mocartsCommented:
you can also inherit your class from TPersistent, but then you must implement all IUnknown methods:
type
  TComponent2 = class(TPersistent, IMyInterface)
  private
    procedure Execute(const aMode: string); stdcall;
    function _AddRef: integer; stdcall;
    function _Release: integer; stdcall;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
  end;
...
implementation
...
function TComponent2._AddRef: integer;
begin
  Result := -1;
end;
function TComponent2._Release: integer;
begin
  Result := -1;
end;
function TComponent2.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then Result := 0 // S_OK
    else Result := HRESULT($80004002);  // E_NOINTERFACE
end;
...
end.

and in exe you must change typecast to TPersistentClass (and correct Create method call):
procedure TForm1.btnFindClassClick(Sender: TObject);
...
begin
...
   // findclass will rise exception we need
   with TPersistentClass(FindClass('TComponent2')).Create do begin
...
end;

hint: you can make some parent class for your all *exported* classes:
type
  TParentClass = class(TPersistent, IUnknown)
  private
    function _AddRef: integer; stdcall;
    function _Release: integer; stdcall;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
  end;

wbr, mo.
0
 
LEEJGAuthor Commented:
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.


0

Featured Post

Become an Android App Developer

Ready to kick start your career in 2018? Learn how to build an Android app in January’s Course of the Month and open the door to new opportunities.

  • 4
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now