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).
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 :)