VCL source code analysis methodology

zhaozj2021-02-08  245

Recently, it seems that the popular source analysis :) I also talk about a little understanding of the analysis method of VCL source in the past period, this article will not explore the architecture of the VCL class and the design model of the design model, just in our Simple instructions for the realization process of common control attributes / methods, I hope to help beginners

VCL analysis method

Example: The origin of the TButton.caption property

(This article is only available to Delphi beginners)

After a while, Delphi's friends will be interested in the VCL source code. I also often see some netizens in major forums to discuss post on VCL source. However, many netizens are trying to understand, but they can last halfway, because they always do not have a head, see the clouds. I also have a habit of watching the source code. When you are fine, you will be right-click, always hope to get some lucky harvest and development skills.

However, there is a basic prevention first, just like people go to school (here, normal people) should come to class, it is generally impossible for primary schools to remember the university, unless he (her) is a genius or special Training. So ggjjddmm, watching the VCL source code is also a basic former question. First you have to be familiar with Win32 API / SDK. If you don't know, you can refer to book "Programming Windows" (Chinese name "Windows programming"). Second, you should be more familiar with Object Pascal, or you have expanded Delphi components (do component development), then I believe that you are already familiar with Object Pascal. Don't be tight, Delphi's online help has a story about Object Pascal. If English is too bad, there is also a lot of enthusiastic networks to translate Chinese help and language reference books.

Oh, I write technical articles like writing prose :)

The subject of the introduction, the theme of our article is the analysis of the VCL source code. Of course, there is a problem with an analysis method. You can't open a source. If you catch a function, you should analyze a function :) So we should also choose, It is a purposeful analysis.

What are the properties that we encountered when we encode our encoding every day? Oh, Name, Caption, Visible, and some controls of Text (such as edit1.text). Then we analyze the CAPTION of the control.

Of course, it is not a CAPTION property of each control, and we use the CAPTION attribute of the TBUTTON class with the TButton class.

Open the Delphi we will use every day, put a button on the form form, get a Button1 button control, press F12 to play Tianyuan program, have you found this code:

Button1: tbutton;

Yes, click on the right button on TButton. In the pop-up context menu, select the first Find Declaration, find the definition of the TButton class, as shown below:

TButton = Class (TButtonControl)

Private

FDEFAULT: BOOLEAN;

Fcancel: boolean;

Factive: boolean;

FMODALRESULT: TMODALRESULT;

Procedure setDefault (value: boolean);

. . . . . .

It turns out that TButton inherits in TButtonControl class, huh, huh :)

Find the CAPTION property of TButton in the object window (Exploring unit.pas window) on the left, as shown below: Double-click the CAPTION property, find the source code of the Caption property, everyone may find that there is no, only one

Property Caption;

Oh, friends who write components know that the method should be read / write text according to the CAPTION property? Where did you go, huh, huh, there is no appearance here, of course, it should be in its parent class (here is just the place where CAPTION is coming out), we will continue in TButtonControl in TButtonControl, and finally we are in TControl class. I found this CAPTION, as for the protected member, I don't have much to say:

protected

Procedure actionchange (Sender: Tobject; Checkdefaults: Boolean; Dynamic;

PROCEDURE Adjustsize; Dynamic;

Procedure Assignto (DEST: TPERSISTENT); OVERRIDE;

PROCEDURE BEGINAUTODRAG; DYNAMIC

Function Canresize (VAR NewWidth: boolean; virtual;

Function Canautosize (VAR NewWidth, Newheight: Integer): Boolean; Virtual;

PROCEDURE CHANGED;

PROCEDURE CHANGESCALE (M, D: Integer); Dynamic;

. . . . . .

Property Caption: Tcaption Read GetText Write SetText Stored Iscaptions;

Take a look at gettext, setText is a function of operating a text property, we find GetText, settext is defined as follows:

Function GetText: TcAption;

Procedure setText (Const value: Tcaption);

There is also TCAPTION, its definition is actually a custom type:

Tcaption = type string;

Note that the clock parameters of the GetText return value and setText are also a string type :)

Let's take a look at the GetText source code:

Function TCONTROL.GETTEXT: TCAPTION;

VAR

Len: integer;

Begin

Len: = gettextlen; // Get the length of the text

SetString (Result, Pchar (NIL), LEN); // Setting Result Returns the length specified by LEN

If Len <> 0 Then GetTextBuf (Pointer (Result), Len 1); // Length is not empty, Result gets text data

END;

If you don't understand the usage of getTextBuf, take a look at the following code:

Procedure TFORM1.BUTTON1CLICK (Sender: TOBJECT);

VAR

Buffer: pchar;

Size: Byte;

Begin

Size: = Edit1.Gettextlen; // Get the text of Edit1

INC (SIZE);

GetMem (buffer, size); // Create an edit1 text length size cache space

Edit1.getTextBuf (Buffer, size); // Get text by cache, the value in the buffer is edit1.textedit2.text: = strpaas (buffer); // buffer Convert to Pascal character type data

FreeMem (buffer, size); // Release memory

END;

The behavior of the above programs is quite:

Procedure TFORM1.BUTTON1CLICK (Sender: TOBJECT);

Begin

Edit2.Text: = Edit1.Text;

END;

Go back to the GetText function, where getTextlen's role is to get text length, GetTextBUF gets text data.

SetText is simpler, defined as follows:

Procedure Tcontrol.Settext (Const Value: Tcaption);

Begin

IF GetText <> Value Then SetTextBuf (Pchar (Value));

END;

It means that if the set value is different from the original, the cache text is reset.

In order to deepen the bottom of the VCL, let's take a look at how GetTextlen is implemented (in fact, the implementation of setTextBuf and GetTextlen):

Function Tcontrol.getTextlen: Integer;

Begin

Result: = Perform (WM_GettextLength, 0, 0); // WM_ dispatched Windows Standard Message

END;

I have to understand that I have to understand it. If I still don't understand (I haven't used Perform), I look at PeForm, what did it do:

Function Tcontrol.Perform (msg: cardinal; wparam, lparam: longint): longint

VAR

Message: tMessage;

Begin

{Your message gives tMessage}

Message.msg: = msg;

Message.wparam: = wparam;

Message.lparam: = lparam;

Message.Result: = 0; // 0 indicates return no processing

If SELF <> nil the windowProc (message); // is not empty, hand over the message to TControl window Process WindowProc processing

Result: = message.result; // Return the result

END;

Here, you will see what WindowProc has done, and WindowProc in TControl is defined in this:

Property WindowProc: TwnotMethod Read FwindowProc Write FwindowProc;

In the CREATE function of TControl:

Constructor Tcontrol.create (Aowner: Tcomponent);

Begin

Inherited Create (Aowner);

FWindowProc: = WndProc;

. . . . . .

It can be seen that we have to find TControl's WndProc process to understand,

The WndProc process is defined as follows:

Procedure WndProc (var message: tMessage; OVERRIDE;

achieve:

Procedure Tcontrol.WndProc (Var Message: TMessage);

VAR

Form: tcustom;

KeyState: TKEYBOARDSTATE; Wheelmsg: Tcmousewheel;

Begin

IF (csdesigning in componentstate) THEN

Begin

Form: = getParentForm (Self);

IF (Form <> nil) and (form.designer <> nil)

Form.designer.isdesignmsg (self, message) Then EXIT

END;

IF (Message.msg> = WM_KEYFIRST) AND (Message.msg <= WM_KEYLAST) THEN

Begin

Form: = getParentForm (Self);

IF (Form <> nil) and form.wantchildkey (self, message) THEN EXIT;

end

Else IF (Message.msg> = WM_Mousefirst) and (Message.msg <= WM_MouseLast) THEN

Begin

IF not (csdoubleclicks in controlstyle) THEN

Case message.msg of

WM_LBUTTONDBLCLK, WM_RBUTTONDBLCLK, WM_MBUTTONDBLCLK:

DEC (Message.msg, WM_LButtondblClk - WM_LButtondown);

END;

Case message.msg of

WM_MOUSEMOVE: Application.hintMouseMessage (Self, Message);

WM_LBUTTONDOWN, WM_LBUTTONDBLCLK:

Begin

IF fdragmode = dmautomatic then

Begin

Beginautodrag;

EXIT;

END;

Include (FcontrolState, CSLbuttondown);

END;

WM_LBUTTONUP:

Exclude (FcontrolState, CSLbuttondown);

Else

WITH MOUSE DO

IF WheelPresent and (RegwheelMessage <> 0) and

(Message.msg = regWheelMessage) THEN

Begin

GetKeyboardState (KeyState);

With Wheelmsg Do

Begin

Msg: = message.msg;

ShiftState: = keyboardStatetoshiftState (KeyState);

Wheeldelta: = Message.wParam;

POS: = TsmallPoint (Message.lparam);

END;

MousewheelHandler (TMely (Wheelmsg));

EXIT;

END;

END;

end

Else if message.msg = cm_visiblechanged then

WITH MESSAGE DO

SenddockNotification (MSG, WPARAM, LPARAM);

Dispatch (Message); // Distribute news

END;

Here mainly talks about the dispatch method, which calls the message of the message according to the incoming message, if the component class and its parent class do not find the handle of the message, the Dispatch method will call the DEFAULTHANDLER (the default message processing method), as follows:

Procedure TOBJECT.DISPATCH (VAR Message); ASM

PUSH ESI

Mov Si, [EDX]

OR Si, Si

JE @@ default

CMP Si, 0C000H

Jae @@ Default

Push EAX

Mov Eax, [EAX]

Call getDynamethod

POP EAX

JE @@ default

MOV ECX, ESI

POP ESI

JMP ECX

@@ Default:

POP ESI

MOV ECX, [EAX]

JMP DWORD PTR [ECX] VMTOFFSET TOBJECT.DEFAULTHANDLER / / Call the default message processing method

END;

The default message is processed as follows, in the System.Pas unit:

Procedure Tobject.defaultHandler (Var message);

Begin

END;

It is seen from the above code to have no processing procedure, track the compilation of Object.defaultHandler execution motion call dword ptr [ECX- $ 10], that is, call Object.defaultHandle, see how to do:

{Object.defaulthandle}

RET

Lea Eax, [EAX $ 00]

That is, a return process!

From the most surface Button.cap, we have come to the compiler layer, you can see that all things can find its solid origin! Based on CAPTION analysis, we can continue to analyze Name properties and other methods / functions.

I hope that this 'prose' can give you a point :)

转载请注明原文地址:https://www.9cbs.com/read-3376.html

New Post(0)