First Article of beta: call an advanced solution by name

Source: Internet
Author: User

Statement: This article is original by Xiong Heng (Beta). If you want to reprint it, please keep itArticleComplete.

Call methods by name advanced solution --- by Xiong Heng (Beta)

Calling methods by name seems to have always been a technology of interest to everyone. 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 published method of a class declared as the method to be called, and obj is
Instance, name, And Param are the names and parameters of the method to be called.

However, this method has a major limitation: Once the tprocedure statement is set, the parameters of the method to be called
The number table must be. If I define multiple methods to be called, and the number, type, and return value of parameters are different,
This method is powerless. In addition, you can use getprocaddress to replace methodaddress.
Similar results, but today we are talking about calling the "method" of the class, and it does not return "method ",
Because getprocaddress can only obtain applicationsProgramThe output (exports) process or function.
If a program or function does not belong to any class, it cannot be called a method ". Of course, the effect is similar, but the limitations are similar.

So if the number and type of parameters can all be changed, will the problem be solved? (If so, this article will not be available .)
Research, I found an effective way: Format function (note, not a DOS command, haha)
Is there no stranger to anyone in the company? Isn't the number and type of parameters passed in variable? As long as the statement is as follows:

Procedure aproc (Param: array of const );

You can call it like this:

Aproc ([123, 'x', true, 'Hello'...]);

Some friends may have said it. It's easy, so it's okay:

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 showed up. Can you give it a try? Something went wrong. (For convenience, we call it our
Executeroutine function, which is a control function. The method to be called is short for the method to be called.) Declaration of this form parameter table
The method is indeed suitable for our control functions, but not for the method to be called. Why? Because the parameter table of the method to be called
This is indeed not the case (array of const. Of course, you said that you have changed all the parameters for the method to be called.
That's not enough? Not to mention how much you need to change (including the parameter table of the function to be tuned and the internal
Now, the key is the internal implementation Part). Let's take a look at the form parameter table of the method to be adjusted after you have changed it.
Appearance. Maybe you don't know what parameters should be passed in. Therefore, we should try
Maintain the parameter table of the method to be called.

Now the problem is converted to the address of the method to be called and the list of its parameters (stored in
Tvarrec array), how to pass parameters when calling it. Some preparations are required:

1. First, let's take a look at the passed parameter table: Params. Its type is called Variable-enable by Delphi.
Put an array (variant open array), equivalent to array of tvarrec, that is, Params is
Array of tvarrec members. In other words, all types of parameters are
Delphi is automatically converted to tvarrec (see Variant open array parameters 1 in help
). According to the definition of tvarrec, the actual data domain stored in tvarrec is 4 bytes, exceeding 4 bytes.
The size of tvarrec is 8 bytes.
Data, 5th bytes as the type ).

2. General information about parameter passing when a function is called (if stdcall is not used ). For general functions
Or process. The first three parameters are placed in eax, EDX, and ECx respectively. If there are more parameters
For the class method, eax is used to store the class instance address, and EDX and ECx respectively store the first two parameters.
Other parameters are added to the stack. Each element in the stack occupies 4 bytes. As mentioned above
The data is also 4 bytes. Just one parameter occupies a seat in the stack for convenient processing. In addition, the result is returned.
To eax.

3. For methods that call the class, there is actually a default hidden parameter self passed in as the first parameter, put
Enter the eax register. Therefore, the first parameter we see is actually the second parameter, so we should pay attention to it during processing.

4. Using objectpascal syntax to call a method, Delphi will automatically help us solve the parameter passing problem, and
You must manually set parameters before calling any function in the Assembly.

Therefore, I decided to use the Embedded Assembly Method to Solve the parameter passing problem: if it is a parameter, put it in edX; if
It is two parameters, respectively placed in edX, ECx; for more than two parameters, use the number of parameters-two cycles according
Next, add the subsequent parameters to the stack. Then, you can call the instance address that has the method to be called through eax.

Function executeroutine (aobj: tobject; aname: string;
Params: array of const): DWORD;
Const
Recsize = sizeof (tvarrec); // The number of bytes that increase progressively when processing the list of parameters in a loop
VaR
Pfunc: pointer;
Parcount: DWORD;
Begin
If not assigned (aobj) then
Raise exception. Create ('Are you sure you want to upload an object? ');
Pfunc: = aobj. methodaddress (aname); // obtain the method address
If not assigned (pfunc) then
Raise exception. createfmt ('method % s' cannot be found, [aobj. classname,
Aname]);

Parcount: = high (Params) + 1; // obtain the number of parameters

ASM
Push ESI // save ESI. We will use it later

MoV ESI, Params // ESI points to the first address of the parameter table
CMP parcount, 1 // determine 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 // loop parcount-2 times
MoV edX, recsize // edX point to the first address of each parameter in sequence, increasing by 8 bytes each time
Add edX, recsize // skip the first two parameters
@ Paramloop:
MoV eax, [esi] [edX] // obtain a parameter using Base Address Change Addressing
Push eax // Add parameters to stack
Add edX, recsize // edX points to the first address of the next Parameter
Loop @ paramloop

@ Twoparams: // Two Parameters
MoV ECx, [esi] + recsize

@ Oneparam: // A PARAMETER
MoV edX, [esi]

@ Noparam:
MoV eax, aobj // input the instance address (that is, hide the parameter self)
Call pfunc // call Method
MoV result, eax // put the returned value into result

Pop ESI // remember to restore
End;
End;

As mentioned above, any type can be inserted into 4 bytes, so the return value is defined as DWORD. You can
Convert data types as needed. This method maximizes the protection of the method to be adjusted, but it is not completely
Only one change needs to be made: the same as the function return value in DLL (do not tell me to reference
Sharemem, which is not covered in this article). If you want to return a long string, change it to pchar,
And pay attention to the necessary space.

The following is an example (remind me again that the method to be tuned must be the published method of a class ):

Tform1 = Class (tform)
Button1: tbutton;
Procedure button1click (Sender: tobject );
Private
{Private Declarations}
Public
{Public declarations}
Published // several methods to be tuned
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', [I, j, k]);
Result: = I + J + K;
End;

Function tform1.fiveint (x1, x2, X3, X4, X5: integer): integer;
Begin
Showmessage (format ('% 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', [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, 'intbool ', [10, 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;

...

The reason why I call this method an advanced solution is not the ultimate solution, because it still has a problem not solved: variable parameters
Problem. But this is not a big problem, because the variable parameter can be replaced with the function return value. Ah? You want to return multiple
? We recommend that you return a pointer to the struct or the simplest object.
Http://www.delphibbs.com/delphibbs/dispq.asp? Lid = 1289838

continue to read the full text of "Beta's first article: 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.