But is there a way to read infomration about the Classes contained in that
package? As far as I can see getpackageinfo is not the right way...
Thanks
Wolfgang Bures
Bures EDV
http://www.bures.at
What would you do with this information? There are two ways to use a package:
statically loaded or dynamically loaded. The first case is the easy one, you
simply Use a unit that has been put into the package as if it were a part of
your application, you name the package in the apps package list and the
linker makes sure the unit is not compiled into your app but linked from the
package at runtime. In this case the package is totally transparent for your
app, you don't need to change a single line of code in the application to
move from a non-packaged to a packaged environment. This is the main use
packages were introduced for.
Dynamically loaded packages are a bit harder to use. Like a DLL a dynamically
loaded package should have a standard, documented interface. 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>
> Thanks
> Wolfgang Bures
> Bures EDV
> http://www.bures.at
>
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.
> 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.
>
Can you guide me to a source explaining interfaces? This is also a new area
for me.
Regards
Eric Harmon's "Delphi COM programming" has a good introduction to interfaces
in general. You can use interfaces without ever touching COM, though.
Remember, if you have a form, most likely, you will need to build both
packages and application with VCL**.BPL.
> > 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.
> >
> Can you guide me to a source explaining interfaces? This is also a new
area
> for me.
I think Peter meant a function that a package exports and the application
imports, in other words calls and gets some information.
in the package:
TForm1 = class(TForm)
...
end;
function GetAForm: TForm;
exports
GetAForm;
implementation
function GetAForm: TForm;
begin
Result := TForm1.Create;
end;
in the application (summarized):
type
TFormFunc = function: TForm;
var
P: TFormFunc;
F: TForm;
begin
H := LoadPackage(...);
P := GetProcAddress(H, 'GetAForm');
F := P;
F.ShowModal;
F.Free;
end;
You can't get classes in a package unless it is programmed to do so. Delphi
calls Register function of every unit that registers classes (by
RegisterClasses or RegisterComponents). (How Delphi determines the entry
point of Register procedures is another issue)
You can only get list of units of a package using some SysUtils functions.
--
Alex Yackimoff | http://yackimoff.cjb.net
unit Processor;
interface
type
TProcessorClass = class of TProcessor;
TProcessor = class ... end;
end.
unit OscillatorProcessor;
interface uses Processor;
type TOscillatorProcessor = class(TProcessor) ... end;
function ProcessorClass: TProcessorClass; begin Result := TOscillatorProcessor end;
exports ProcessorClass;
end.
package SineOscillatorProcessorPackage;
contains SineOscillator;
end.
program SoftSynth;
uses ShareMem, Processor, SysUtils, Windows;
var
Package: HModule;
ProcessorClass: function: TProcessorClass;
begin
Package := LoadPackage('SineOscillatorPackage.BPL');
ProcessorClass := GetProcAddress(Package,'ProcessorClass');
with ProcessorClass.Create() do
begin
// we have a TSineOscillatorProcessor
Free;
end;
end.
type
TEnumTypeInfoCallback = function(UserData: Pointer; Info: PTypeInfo):
Boolean; register;
function GetBaseOfCode(Module: hModule; var CodeStart, CodeEnd: PChar):
Boolean;
asm // get Codesegment pointers, check if module is a valid PE
PUSH EDI
PUSH ESI
AND EAX,not 3
JZ @@2
CMP Word Ptr [EAX],'ZM';
JNE @@1
MOV ESI,[EAX + 03Ch]
CMP Word Ptr [ESI + EAX],'EP'
JNE @@1
MOV EDI,[EAX + ESI + 014h + 008h]
ADD EAX,[EAX + ESI + 014h + 018h]
ADD EDI,EAX
MOV [EDX],EAX
MOV [ECX],EDI
XOR EAX,EAX
@@1: SETE AL
@@2: POP ESI
POP EDI
end;
function EnumTypeInfo(Module: hModule; Callback: TEnumTypeInfoCallback;
UserData: Pointer): PTypeInfo;
var
P,E,K,N: PChar;
L: Integer;
begin
Result := nil;
if Assigned(Callback) then
try
if GetBaseOfCode(Module, P, E) then
while P < E do
begin
DWord(P) := DWord(P) and not 3;
K := P + 4;
if (PDWord(P)^ = DWord(K)) and (PByte(K)^ > 0) and (PByte(K)^ < 18)
then // Info.Kind in ValidRange.D6
begin
L := PByte(K + 1)^; // length Info.Name
N := K + 2; // @Info.Name[1]
if (L > 0) and (N^ in ['_', 'a'..'z', 'A'..'Z']) then // valid
ident ??
begin
repeat
Inc(N);
Dec(L);
until (L = 0) or not (N^ in ['_', 'a'..'z', 'A'..'Z',
'0'..'9']);
if L = 0 then // length and ident valid
if Callback(UserData, Pointer(K)) then // tell it and if
needed abort iteration
begin
Result := Pointer(K);
Exit;
end else K := N;
end;
end;
P := K;
end;
except
end;
end;
procedure Test;
function MyEnum(Data: Pointer; Info: PTypeInfo): Boolean; register;
begin
WriteLn( Info.Name );
end;
begin
EnumTypeInfo(MainInstance, @MyEnum, nil);
// or
EnumTypeInfo(GetModuleHandle('vcl50.bpl'), @MyEnum, nil);
end;
Hagen