Delphi Call method Advanced Solution by name

Source: Internet
Author: User

Delphi Call method Advanced Solution by name Blog Category:
    • Programming Basics
DELPHIJ#ASPDOS Data structure

Calling methods by name always seems to be the technology that people pay more attention to, and there is a classic answer on the forum:

Type
Tprocedure = procedure (test:string) of object;

Procedure Executeroutine (Obj:tobject; Name, param:string);
Var
Pmethod:tmethod;
Aprocedure:tprocedure;
Begin
Pmethod.data: = Pointer (OBJ);
Pmethod.code: = obj.methodaddress (Name);
If Assigned (Pmethod.code) Then
Begin
Aprocedure: = Tprocedure (Pmethod);
Aprocedure (Param);
End
End

Use: The method to be called is declared as the published method of a class, and OBJ is the class that owns the method to be called.
instance, name and Param are the names and parameters of the method to be called respectively.

But there is a big limitation to this approach: once the Tprocedure declaration is set, the parameters of the method to be called
The tables are bound to be. If I define multiple methods to be called, and the number, type, and return values of the parameters are different,
Then there is nothing to do with this approach. Another: With GetProcAddress instead of methodaddress can also be real
This is a similar effect, but what we are discussing today is the "method" of invoking the class, and it is not returning the "method",
Because GetProcAddress can only get the process or function of the output (exports) of the application, these
Process or function does not belong to any class, it is not called "method". Of course, the effect is similar, but the limitations are similar:-(

So what if the number of arguments, the type is variable can not be solved? (If this is not the case) by
Research, I found an effective way: the Format function (note, not DOS command, hehe) phase
You are not familiar with the letter, the number and type of parameters passed in it is not variable? Just declare as follows:

Procedure Aproc (Param:array of Const);

You can call this:

Aproc ([123, ' X ', True, ' hello ' ...]);

A friend may have to say, that is not easy, so it is not possible:

Type
Tprocedure = procedure (Params:array of const) of object;

Procedure Executeroutine (Obj:tobject; name:string; Params:array of Const);
Var
...
Begin
...
Aprocedure (Params);
...
End

Don't worry, the problem just appeared, you run a try? It's a problem. (For the sake of convenience, temporarily call our
The Executeroutine function, which is the control function; The declaration of the formal parameter list is the method to be called.
The approach does fit our control function, but it is not suitable for the method to be tuned. Why? Because the formal parameter list of the method to be tuned
That's not true (array of const). Of course, you said you'd change the formal parameter list of all the methods to be adjusted.
Does this look like it? And don't say how many things you need to change (including the parameter table of the function to be tuned and the internal real
Now, the key is the internal implementation part), just look at you changed the method to adjust the formal parameter list, all become a
Appearance Maybe you don't know exactly what parameters to pass in. Therefore, we should try to
Maintains the formal parameter list of the method to be tuned.

Now the problem is converted to the address of the method that is known to be tuned in the control function and its argument list (stored in a
In the Tvarrec array), how to pass the arguments in when it is called. This requires a few preliminary knowledge:

1. First let's take a look at this parameter list: Params. Its type is called variable-open by Delphi.
Array of Tvarrec (Variant Open array), which means that the Params is a
A member is an array of Tvarrec. In other words, when parameters are passed into the Params, various types are
Delphi Automatic conversion for TVARREC (see the Help in Variant open array parameters a
section). Look at the definition of Tvarrec, it actually stores a data field of 4 Bytes, more than 4 Bytes
The only pointers to keep in mind is that the size of the TVARREC is 8 Bytes (before the study found the first 4 Bytes stored
Data, 5th Byte is the type).

2. General case of parameter passing when calling function (without using stdcall). For a general function
or process, the first three parameters are placed in EAX, EDX, ECX, and later if there are more parameters, on the stack
For the class method, the EAX is fixed for the address of the class instance, and EDX and ECX store the first two parameters respectively.
Number, and the rest of the arguments into the stack. Each element in the stack occupies 4 Bytes, and the previous one says that the tvarrec stored in the
The data is also 4 Bytes, just one parameter in the stack to occupy a seat, easy to handle. In addition, the result returns
Into the EAX.

3. For the method that invokes the class, there is a default hidden parameter, self, passed in as the first parameter, put
into the EAX register. So the first parameter we see is actually the second one, so we have to be careful when we deal with it.

4. Using objectpascal syntax to invoke the method, Delphi will automatically help us deal with the problem of the transfer of parameters, and
Before calling any function in the assembly, you need to set the parameters manually.

Therefore, I decided to use an inline assembler to solve the parameter transfer problem: If it is a parameter, add EDX;
Two parameters, respectively, into the EDX,ECX, for more than two parameters, with the number of parameters-2 cycles depending on
The next parameter in the stack. Then the instance address with the method to be tuned is passed into the EAX, and it can be call.

function Executeroutine (aobj:tobject; aname:string;
Params:array of Const): DWord;
Const
Recsize = SizeOf (TVARREC); Number of bytes incremented when looping the parameter list
Var
Pfunc:pointer;
Parcount:dword;
Begin
If not Assigned (aobj) Then
Raise Exception.create (' Are you sure you're passing in an object? ‘);
PFunc: = aobj.methodaddress (AName); Get method Address
If not Assigned (PFunc) Then
Raise Exception.createfmt (' Method for%s ' cannot be found:%s ', [Aobj.classname,
AName]);

Parcount: = High (Params) + 1; Get the number of arguments

Asm
PUSH ESI//save ESI, we'll use it later.

MOV esi, Params//ESI pointing to the first address of the parameter table
CMP Parcount, 1//Judging the number of parameters
JB @NoParam
JE @OneParam
CMP Parcount, 2
JE @TwoParams

@ManyParams://More than two parameters
CLD//Clear Direction flag
MOV ECX, Parcount
Sub ECX, 2//Cycle ParCount-2 times
MOV edx, Recsize//edx points to the first address of each parameter in turn, incrementing 8 Bytes at a time
ADD EDX, recsize//skip top two parameters
@ParamLoop:
MOV EAX, [Esi][edx]//Get a parameter with base address address method
PUSH EAX//parameter in stack
ADD edx, recsize//EDX refers to the first address of a parameter down
LOOP @ParamLoop

@TwoParams://two parameters
MOV ECX, [ESI] + recsize

@OneParam://One parameter
MOV EDX, [ESI]

@NoParam:
MOV EAX, Aobj//Incoming instance address (i.e., hide parameter self)
Call PFunc//calling method
MOV result, EAX//return value put into result

POP ESI//Remember to restore
End
End

As already mentioned, any type can be stuffed into 4 Bytes, so the return value is defined as a DWord and you can
Type conversions to suit your needs. This approach maximizes the protection of the method to be adjusted, but it is not completely
With modifications, only one place needs to make the appropriate adjustment: the same as the function return value in the DLL (Don't tell me to reference
Sharemem, which does not fall within the scope of this article), if you want to return a long string, change to PChar,
and take care to apply for the necessary space.

Here is an example of use (again, the method to be tuned must be a published method for a class):

TForm1 = Class (Tform)
Button1:tbutton;
Procedure Button1Click (Sender:tobject);
Private
{Private declarations}
Public
{Public declarations}
Published//Several methods to be adjusted
function Towint (I, J:integer): Integer;
function Threeint (I, J, K:integer): Integer;
function Fiveint (X1, X2, X3, X4, X5:integer): Integer;
function Threechar (I, J, K:char): PChar;
function Twostr (X, y:string): PChar;
function Intnbool (I:integer; B:boolean): Boolean;
End

...

function Executeroutine (aobj:tobject; aname:string;
Params:array of Const): DWord;
...

function Tform1.towint (I, J:integer): Integer;
Begin
ShowMessage (Format ('%d +%d ', [I, J]));
Result: = I + J;
End

function Tform1.threeint (I, J, K:integer): Integer;
Begin
ShowMessage (Format ('%d +%d +%d ', [I, J, K]));
Result: = I + J + K;
End

function Tform1.fiveint (X1, X2, X3, X4, X5:integer): Integer;
Begin
ShowMessage (Format ('%d +%d +%d +%d +%d ', [X1, X2, X3, X4, X5]));
Result: = X1 + X2 + X3 + X4 + X5;
End

function Tform1.threechar (I, J, K:char): PChar;
Var
res:string;
Begin
ShowMessage (Format ('%s +%s +%s ', [I, J, K]));
Res: = I + J + K;
Result: = AllocMem (Length (Res) + 1);
Strpcopy (Result, Res);
End

function Tform1.twostr (X, y:string): PChar;
Var
res:string;
Begin
ShowMessage (Format ('%s +%s ', [X, Y]));
Res: = X + Y;
Result: = AllocMem (Length (Res) + 1);
Strpcopy (Result, Res);
End

function Tform1.intnbool (I:integer; B:boolean): Boolean;
Begin
If B Then
ShowMessage (IntToStr (I) + ' and True ')
Else
ShowMessage (IntToStr (I) + ' and False ');

Result: = B;
End

Procedure Tform1.button1click (Sender:tobject);
Var
I:integer;
B:boolean;
s:string;
Begin
I: = Executeroutine (self, ' threeint ', [10, 23, 17]);
ShowMessage (' Result: ' + inttostr (i));

I: = Executeroutine (self, ' fiveint ', [1, 2, 3, 4, 5]);
ShowMessage (' Result: ' + inttostr (i));

B: = Boolean (Executeroutine (self, ' intnbool ', [Ten, False]));
If B Then
ShowMessage (' Result:true ')
Else
ShowMessage (' Result:false ');

S: = PChar (Executeroutine (self, ' Threechar ', [' A ', ' B ', ' C ')]);
ShowMessage (' Result: ' + s);

S: = PChar (Executeroutine (self, ' twostr ', [' Hello ', ' world ']);
ShowMessage (' Result: ' + s);
End

...

I call this approach a high-level solution, not the ultimate, because it still has a problem unresolved: the argument
Problem. But this is not a big problem, because the function return value can be used instead of the argument. Huh? You want to return more
A value? It is recommended that you return a pointer to the struct, or one of the simplest objects.

Delphi Call method Advanced Solution by name

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.