Update history: No.1
Update time: 2001-11-01 20:09
Updated by: Musicwind®
Update note: First draft completed.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
summary:
This article discusses how to export classes in a Dll - many subclasses based on a specific abstract class. This technology uses the idea of polymorphism, allowing us to get effects similar to plug-ins.
Intended readers:
Understand the concept of polymorphism; understand the concept of metaclass.
Technical difficulty:
6/10 .
To export a class from a Dll, the first thing you must think of is to use the bpl package. One disadvantage of this method is that the user must know which classes are contained in the package, which means that the name of the class must be known - this is a limitation in a certain sense. Imagine a situation where the user defines a bottom layer abstract class, and then many application classes (concrete class), then, for the user, he hopes to be able to use these classes without knowing the specific classes - it seems a bit mysterious to say this, but the actual situation is indeed the case, because it cannot be predicted when defining abstract classes How many specific classes will there be in the future? So what kind of technology will be needed to realize such a demand?
In fact, the technical difficulty of implementing it is not very difficult - the author here dedicates his practical experience to everyone, as a way to attract new ideas, and hopes to see other better methods!
The following first introduces some basic knowledge involved in this method, and then uses an example to illustrate the specific implementation.
1. Basic concepts
Meta class (meta class), also called class-reference type (class-reference type), can be regarded as a type of class, and the value of a variable declared with this type represents a class. for example:
type
TClass = Class of TObject;
This declares a metaclass type. Then you can have variable declarations like this:
Var
AClass: TClass;
Then, you can use it like this:
AClass := TObject;
or:
AClass := TButton;
or:
AClass := TForm;
etc.
Because TClass is a metaclass of type TObject, and TButton, TForm, etc. are all derived from TObject, values such as TButton and TForm are acceptable to AClass.
Then, we can use the idea of polymorphism and flexibly use the class variable AClass. This is also the basic knowledge for the specific implementation below.
2. Specific implementation
The first step is to create an abstract class:
We use such a simple declaration. The abstract class only provides an abstract method, but it does not affect our description of the problem:
TMyBaseForm = Class(TForm)
PRotected
function GetTitle: pchar; virtual; abstract;
end;
MyBaseFormClass = Class of TMyBaseForm;
Let’s not discuss how many practical methods and interfaces such an abstract class provides, because what we want to discuss is a technical feasibility. Assume that the author's original intention in defining this interface is to obtain any number of changing Titles, and the specific return value of GetTitle needs to be implemented by subclasses. Moreover, the author also hopes that the code of the subclass can be implemented in Dll and separated from the main program - this method has a plug-in flavor, and may also be able to realize some features of Plug&Play - isn't it quite attractive? So, what should you do next?
First, the main program and Dll program should include the units declared above. Then, the main program is responsible for implementing a driver - dynamically loading Dll and dynamically loading classes; and Dll is responsible for implementing subclasses.
Let’s talk about Dll first. What should Dll do?
The second step is to export the subclass in Dll :
We designed the following two exported functions:
1. function GetClassCount: integer; stdcall;
Tell the caller that there are several subclasses in this Dll;
2. function GetClassTypeByIndex(const iIndex: integer;
var ClassType: MyBaseFormClass): WordBool; stdcall;
Get a specific subclass by index. Note that the type of ClassType here is MyBaseFormClass, which indicates that its value will be a definite class inherited from TMyBaseForm.
Here is one possible implementation of them:
function GetClassCount: integer;
begin
result := 3; //Indicates that 3 classes are exported in this Dll
end;
function GetClassTypeByIndex(const iIndex: integer;
var ClassType: MyBaseFormClass): WordBool;
begin
result := True;
case iIndex of
0: ClassType := TFrmTest1;
1: ClassType := TFrmTest2;
2: ClassType := TFrmTest3;
else
result := False;
end;
end;
Of course, the units where TFrmTest1, TFrmTest2 and TFrmTest3 are located should be included in the Use list of the unit. The implementation of TFrmTest1 can be like this:
TFrmTest1 = Class(TMyBaseForm)
protected
function GetTitle: PChar; override;
end;
function TFrmTest1.GetTitle: Pchar;
begin
result := 'Hello from TFrmTest1';
end;
Finally, don't forget to add GetClassCount and GetClassByIndex to the Exports list. Then, when building the Dll project, please check "use runtime package" in the Project option-package. The specific reasons will be discussed later.
At this point, the work on Dll has come to an end.
The third step is the implementation of the main program driver engine:
This step is relatively easy - it is nothing more than dynamically loading the Dll, then calling the GetClassCount function, and then calling GetClassByIndex. Key code:
Var AClass: TMyBaseClass;
AForm: TMyBaseForm;
I, iCount: integer;
blResult: Boolean;
begin
//Omit the part about loading the dynamic library, assuming that FPGetClassProc points to the GetClassCount function and FPGetClassByIndexProc points to GetClassByIndex, then:
iCount := FPGetClassProc;
for I := 0 to iCount ?C 1 do
begin
AClass := FPGetClassByIndex(I, blResult);
if blResult then
begin
AForm := AClass.Create(application);
AForm.Caption := AForm.GetTitle;
AForm.Show;
end;
end;
//…
end;
Note that, similar to Dll, you also need to choose to use the runtime package when creating the output file. This is because not using the runtime package will result in multiple copies of the same class in memory, so using the Is operator on them will return a False result.
Musicwind®@HangZhou.Zhejiang.China
2001-11-01
More articles
[ End of article]