?
Solved

URGENT: Creating package containing objects, loading it and using the object...

Posted on 2003-03-25
7
Medium Priority
?
385 Views
Last Modified: 2010-04-04
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
Comment
Question by:LEEJG
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 2
7 Comments
 
LVL 9

Expert Comment

by:mocarts
ID: 8203751
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
 

Author Comment

by:LEEJG
ID: 8209107
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
 
LVL 9

Expert Comment

by:mocarts
ID: 8209188
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
Technology Partners: 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!

 
LVL 1

Expert Comment

by:stepashka
ID: 8209278
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
 
LVL 9

Expert Comment

by:mocarts
ID: 8209698
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
 
LVL 9

Accepted Solution

by:
mocarts earned 2000 total points
ID: 8209841
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
 

Author Comment

by:LEEJG
ID: 8210495
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

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Michael from AdRem Software explains how to view the most utilized and worst performing nodes in your network, by accessing the Top Charts view in NetCrunch network monitor (https://www.adremsoft.com/). Top Charts is a view in which you can set seve…
Do you want to know how to make a graph with Microsoft Access? First, create a query with the data for the chart. Then make a blank form and add a chart control. This video also shows how to change what data is displayed on the graph as well as form…
Suggested Courses
Course of the Month11 days, 7 hours left to enroll

752 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