In the Delphi help I found the command LoadPackage. This command should
load the package at runtime of the mainform without compiling together
with this package. Is this right?
How can I then access to the package forms if they are not declared in
USES?
Can anyone give me help how to load packages at runtime and then accecc
to the forms (e.g. show the forms)?
Thank you.
Lutz Weder
A dynamically loaded package should have
a standard, documented interface, like a DLL. The interface consists of
a set of exported functions (exported via Exports clauses, use Name
clauses on the exports statements to force the functions to be exported
by ungarbled names). You connect to these functions via GetProcAddress,
like you would for a DLL. In the design i prefer the package exports
only one function and that returns an instance of a standard interface
type the package is required to support. All further access (like
enumerating supported classes for the user to pick from, or creating an
instance of such a class) is done via methods of the interface.
An alternative scheme is to have a central registration package that is
used statically by application and dynamically loaded packages. The
loaded package registers the classes it has available with the
registration package and the app uses methods of the registration
package (for which it can directly Use a unit containing the stuff) to
create instances of the registered classes. The registration unit would
go considerable beyond of what RegisterClass/FindClass gives you, i.e.
you can ask it "which classes does package XYZ support" and thus create
a class instance without having to know the name of the class up front.
There is a good article on building package-based frameworks on
http://www.xapxone.com/html/builderpattern.htm
This older post may also give you an example about the exported function
interface for a package:
<quote>
MDI child forms in a package, dynamcially loaded
Create a new project group, add a new form, set formstyle to fsMDIForm,
rename the form to MDIMainForm, add a TToolbar, add a button to it,
double-click on button to create OnClick handler. Switch to unit, add a
private field hPackage: THandle to the form, modify OnClick handler as
procedure TMDIMainForm.ToolButton1Click(Sender: TObject);
var
createProc: Procedure;
begin
If hPackage = 0 Then
hPackage := LoadPackage( ExtractFilePath( Paramstr(0) )+
'ChildPackage.bpl' );
If hPackage = 0 Then
ShowMessage('LoadPackage failed')
Else Begin
@createProc := GetProcAddress( hPackage, 'CreateChildForm' );
If Assigned( CreateProc ) Then
CreateProc
Else
ShowMessage('GetProcAddress failed');
End;
end;
Save the project as MDIMain.dpr. Go into the project options and make
sure the "compile with run-time packages" checkbox is checked.
Add a new package to the project group, add a form to the package, set
its style to fsMDIChild, name it MDIChildform, change to the unit and
modify the Implementation section like this
Procedure CreateChildform;
Begin
TMDIChildform.Create(Application).Show;
End;
exports
CreateChildform name 'CreateChildForm';
initialization
end.
Save the package as ChildPackage. Open its Options dialog and set the
BPL output directory to the projects directory (so the BPL ends up in
the same directory as the main EXE). Build all projects in the group.
Activate and run the EXE, hit button -> new child appears, no
exceptions <g>.
</quote>
Peter Below (TeamB) 10011...@compuserve.com)
No e-mail responses, please, unless explicitly requested!
Note: I'm unable to visit the newsgroups every day at the moment,
so be patient if you don't get a reply immediately.
Create a run-time package named 'package1.bpl' which contains any number of
forms units. In each of those units, declare the following function in the
interface section (the function name is case sensitive):
function GetFormClass: TFormClass;
and implement it in the implementation section:
function GetFormClass: TFormClass;
begin
Result := TMyForm; // the form type in the specific unit
end;
Now, Create the main application (compile it with run-time package): Place a
TButton plus a TListBox on the main form and connect the events in the
following code.
Add this declaration in the private section of TForm1:
h: HModule;
Pressing the button will load the package, extract the form classes names
and put them in the list box. Double clickling on one of the form names in
the listbox will
launch that form.
procedure PkgInfoCallBack(const Name: string; NameType: TNameType; Flags:
Byte; Param: Pointer);
type
TGetFormClass = function:TFormClass;
var
fptr: TGetFormClass;
aFormClass: TFormClass;
begin
if NameType = ntContainsUnit then
begin
{ the entry for the 'GetFormClass' function in the package will have
the form:
'@Unitname@GetFormClass$qqrv'
Use TDump or QuickView to confirm because modifier like 'stdcall'
will
affect the actual encoding. Take note that the unit name is all
lowercase
except for the 1st char }
fptr :=
GetProcAddress(Form1.h,PChar(Format('@%s%s@GetFormClass$qqrv',[UpCase(Name[1
]),LowerCase(Copy(Name,2,MaxInt))])));
if assigned(fptr) then
begin
aFormClass := fptr;
Form1.ListBox1.Items.AddObject(Name+'.'+aFormClass.ClassName,TObject(aFormCl
ass));
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
aForm: TForm;
i: integer;
begin
h := LoadPackage('package1.bpl');
if h > 0 then
GetPackageInfo(h,nil,i,PkgInfoCallBack);
end;
procedure TForm1.ListBox1DblClick(Sender: TObject);
var
aFormClass: TFormClass;
aForm: TForm;
begin
aFormClass := TFormClass(ListBox1.Items.Objects[ListBox1.ItemIndex]);
aForm := aFormClass.Create(nil);
aForm.ShowModal;
aForm.Free;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if h > 0 then
UnloadPackage(h);
end;
So you see, the main idea is to have some kind of procedure/function to
register the actual class references types contained in the package. If the
register procedure/function has parameters, verify with Tdump (with cmd line
param '-m') or QuickView to see the actual name mangling used by Delphi.
Charles
(remove NO SPAM from address to reply by email)
Regards from Germany
Lutz Weder