Delphi custom message application

Source: Internet
Author: User

Interface

Uses
Windows, messages, sysutils, variants, classes, graphics, controls, forms,
Dialogs, stdctrls; const
Windowsmessage = wm_user + 101; Procedure userpro (VAR message: tmessage): Message windowsmessage
Userpro is the name of the custom message processing function, and windowsmessage is a Windows message constant or custom message constant.
---- Then add the message processing function to the program. The general format of the function is as follows:

Procedure userclass. userpro (VAR message: tmessage );
VaR
// Add Definition
Begin
// Add the Program Statement
End;
Userclass is the name of the class that encapsulates the custom message processing function.
---- Finally, the custom message processing function of Delphi involves the message structure of Windows. I will not talk about it here. You can refer to the help information of Windows API and Delphi. I believe that if you have mastered the custom message processing functions of Delphi, You can compile programs that are as simple as VB and powerful as C ++. Nowadays, many applications have such a function. When a user chooses to minimize a window, the window is not minimized to the taskbar as usual, but is "minimized" into a taskbar icon. Such features are available as FoxMail 3.0 NetVampire 3.0. The implementation of this function is actually not complicated. When the window is minimized, the window will send the WM_SYSCOMMAND message, as long as you need to intercept the Windows WM_SYSCOMMAND message, when the window is minimized, hide the window and call the WindowsAPI function shell_policyicon to add the defined icon to the taskbar. The function Definition of shell_policyicon is as follows: function shell_policyicon (dwMessage: DWORD; lpData: ppolicyicondata ): BOOL; stdcall; the parameter dwMessage specifies the shell_policyicon function operation, which can be one of the three values NIM_ADD NIM_DELETE NIM_MODIFY, corresponding to the action of adding, deleting, and modifying icons respectively.

---- The ppolicyicondata structure pointed to by the lpData parameter is defined as follows:

_ Policyicondataw = record
CbSize: DWORD;
Wnd: HWND;
UID: UINT;
UFlags: UINT;
UCallbackMessage: UINT;
HIcon: HICON;
SzTip: array [0 .. 63] of WideChar;
End;

Tpolicyicondata = _ policyicondataw;
---- In this structure, Wnd indicates the window to which the message belongs. UCallbackMessage indicates the callback message. If Wnd and uCallbackMessage are specified, the user has actions on the taskbar icon (such as clicking the icon, move the cursor on the icon ). The system sends the uCallbackMessage to the window specified by Wnd. HIcon is the handle of the icon to be added, and szTip is the prompt line of the icon (that is, the text displayed in a small yellow box when the cursor is moved to the icon ). Message. To implement the above functions, the most important thing is to process WM_SYSCOMMAND messages and custom icon messages. These messages do not have corresponding events in Delphi. Here, you need to use the custom message processing function of Delphi to intercept and process these messages.

---- First look at the following program. Before executing the program, you must first change the Icon attribute of Form1 and add an Icon to Form1. Otherwise, a blank area will appear on the taskbar.

Unit Unit1;
Interface
Uses
Windows, Messages, SysUtils,
Classes, Graphics, Controls, Forms,
Dialogs, ShellAPI;

Const
WM_BARICON = WM_USER + 200;

Type
TForm1 = class (TForm)
Private
Procedure WMSysCommand (var
Message: TMessage); message WM_SYSCOMMAND;
Procedure WMBarIcon (var
Message: tmessage); message wm_baricon;
{Private Declarations}
Public
{Public declarations}
End;

VaR
Form1: tform1;

Implementation

{$ R *. DFM}
Procedure tform1.wmsyscommand
(VAR message: tmessage );
VaR
Lpdata: pnotifyicondata;
Begin
If message. wparam = SC _icon then
Begin
// If the user minimizes the window, the window
Hide and add icons on the taskbar
Lpdata: = new (pnotifyicondataa );
Lpdata. cbsize: = 88;
// Sizeof (pnotifyicondataa );
Lpdata. WND: = form1.handle;
Lpdata. hicon: = form1.icon. Handle;
Lpdata. ucallbackmessage: = wm_baricon;
Lpdata. uid: = 0;
Lpdata. sztip: = 'samples ';
LpData. uFlags: = NIF_ICON
Or NIF_MESSAGE or NIF_TIP;
Shell_policyicon (NIM_ADD, lpData );
Dispose (lpData );
Form1.Visible: = False;
End
Else
Begin
// If it is another SystemCommand
The system calls the default processing function to process the message.
DefWindowProc (Form1.Handle, Message.
Msg, Message. WParam, Message. LParam );
End;
//
End;

Procedure TForm1.WMBarIcon (var Message: TMessage );
Var
LpData: PNotifyIconData;
Begin
If (Message. LParam = WM_LBUTTONDOWN) then
Begin
// If you click the taskbar icon, delete the icon and reply to the window.
LpData: = new (PNotifyIconDataA );
LpData. cbSize: = 88; // SizeOf (PNotifyIconDataA );
LpData. Wnd: = Form1.Handle;
LpData. hIcon: = Form1.Icon. Handle;
LpData. uCallbackMessage: = WM_BARICON;
LpData. uID: = 0;
LpData. szTip: = 'samples ';
LpData. uFlags: = NIF_ICON or NIF_MESSAGE or NIF_TIP;
Shell_policyicon (NIM_DELETE, lpData );
Dispose (lpData );
Form1.Visible: = True;
End;
End;

End.
---- Run the above program and click the minimization button on the title bar of the program window. You can see that the window is "minimized" into a taskbar icon. Click the icon and the window will return to its original state.

---- As you can see from the above program, the implementation of the custom message processing function of Delphi is also very simple. First, add the definition of the custom message processing function to the Private definition of the Form class, the definition is as follows:

Procedure UserPro (Var Message:
TMessage): message WindowsMessage
UserPro is the name of the custom message processing function,
WindowsMessage is a Windows message constant or a custom message constant.
---- Then add the message processing function to the program. The general format of the function is as follows:

Procedure UserClass. UserPro (Var Message: TMessage );
Var
// Add Definition
Begin
// Add the Program Statement
End;
Userclass is the name of the class that encapsulates the custom message processing function.
---- Finally, the custom message processing function of Delphi involves the message structure of windows. I will not talk about it here. You can refer to the help information of Windows API and Delphi. I believe that if you have mastered the custom message processing functions of Delphi, You can compile programs that are as simple as VB and powerful as C ++.

//////////////////////////////////////// /////////////////////////////////////
In the last few days, I have read the implementation of the windows Message Mechanism in Delphi in the book "Inside VCL" by Li Wei. This is the second time. I have fully understood it when I read it for the first time, but I did not take notes, so I read it again. This time I made up my mind to write a note. At the same time, I was lucky to have read two articles about the Delphi message framework while wandering online. Some of them have been deeply analyzed and a very good summary of the Delphi message mechanism. Now I don't want to take notes, but I thought they did well in writing these articles, but they didn't write them by myself. To clarify the line of message mechanism, you have to write your own pen. It is really understandable to express what you think you understand. I have to say a few more words here. In many aspects of Li Wei's book, I don't think it is well written. Sometimes, although a topic uses N length to write, it seems that some parts of the above article have not been thoroughly written. For example, I think the expression of makeobjectinstance is not good at all, so do not worship the so-called boss.
In many parts of this article, I will write it very easily, because I have clearly described it in Li Wei's book and the articles I have referenced. Therefore, I will not pay more attention to it, just link the entire process and write several valuable examples.

Body
Always remember that whether you use the SDK or use VCL to create a window, you must follow the Windows game rules, that is, first register the window class, and then create a window instance, write the implementation code in the message loop. You also need to know that Windows has pre-registered multiple window classes for us, such as "Edit" and "ComboBox". What we need to do at this time is to create these Windows directly without registering the window class; in Delphi, all this is simpler. VCL is ready for you. You just need to drag the control you want on the design form and then write the implementation code. Is it cool?

1. Create a window
In VCL, all real window controls with the Handle attribute inherit from TWinControl, starting from the creation of TWinControl.
In VCL, the window is not created according to the process we imagined. That is, all windows are created first and then called, but created only when necessary. You may not be able to understand the meaning of this sentence. The window control inherited from TWinControl has the Handle attribute. When the Handle value is required in the Code, the getter of this attribute calls TWinControl. handleNeeded to obtain the handle. If the form has been created, the handle is directly returned. Otherwise, the window instance is created first and then the handle is returned. Therefore, the window is created in TWinControl. implemented in HandleNeeded. The goal of Borland is to save system resources to the greatest extent.
There are several important methods in TWinControl. HandleNeeded to create a window. TWinControl. HandleNeeded calls TWinControl. CreateHandle to get Handle. However, CreateHandle is only a packaging function. It first calls TWinControl. createWnd is an important process. It first calls TWinControl. createParams sets the parameters for the window to be created. These parameters call the RegisterClass API to register the window class. CreateWnd then calls TWinControl. create‑whandle and create‑whandle are the functions that actually call the create‑wex API to create a window instance. CreateHandle, CreateWnd, CreateParams, and create‑whandle are all virtual methods. The Derived classes can reload these methods to obtain more functions. The maximum chance of CreateParams being overloaded is true.
I suggest you carefully read the source code of the method mentioned above to deepen your impression. You also need to look at the source code for the method I mentioned later, and I will not remind you again.
At this point, a window is created, but it still cannot run correctly because it does not have a message loop.

II. Implementation of message loop
The implementation of message loops is the most brilliant place in the entire VCL message framework, because the traditional Windows callback function is a static function, and the form in VCL is a class. When calling a class method, apart from the address of the function itself, we also need a Self. It is not easy to establish associations between them. It requires a lot of code skills, at the same time, the message loop must ensure that hundreds to tens of thousands of messages can be processed every second, so the code needs to be well written. It may take a lot of time to study this part of the code.
We know that the entry address of the form callback function is required when registering the Form class. We can imagine that this process occurs in TWinControl in VCL. in the CreateWnd call, In this method, the static function pointer @ InitWndProc is assigned to lpfnWndProc in the wndclassex structure, which is the first place where the VCl form establishes a message loop.
When InitWndProc is called for the first time, use SetWindowLong API to replace the message callback function with TWindowControl. FObjectInstance, and TWinControl. FObjectInstance is a normal Pointer, and the value assigned is in TWinControl. in Create, the most Magic function MakeObjectInstance is used. This process is very complicated. For details, see [3].
The replacement result is that the class method TWinControl. MainWndProc becomes the real message processing Handler, and the subsequent message processing of the corresponding form instance is completed in TWinControl. MainWndProc. Another detail is that before the message is processed by MainWndProc, you need to call a static function-StdWndProc-to distribute the message in a unified manner [1]. So far, the transformation from a common static function to a class method has been completed.
In fact, TWinControl. MainWndProc calls WindowsProc to actually process window messages. In TControl. Create, WindowsProc is specified as the virtual method WndProc in the class. On the Inheritance chain from TControl to the actual VCL form class, many derived classes overload WndProc, so that each derived class that loads this method will add some functions. Of course, at the end of the inheritance chain, such as TForm, you can also overload WndProc to complete some tricky code. Remember, if you reload WndProc, you always process the messages you want first, and then submit the unprocessed messages to the WndProc of the parent class for processing.
In the wndproc of each inheritance class, only the most basic messages that maintain the operation of the form should be processed. Other unprocessed messages will be passed to tobject. diapatch in tcontrol. wndproc. Tobject. diapatch queries the corresponding message ID in the dynamic method table of the parent class. If the ID is found, the corresponding method is called. All the class methods for message processing should be defined with the keyword message. This ensures that the entry address is in a dynamic method table and that the messages to be processed can be stored in tobject. diapatch is called during execution.
If the messages to be processed cannot be queried in the dynamic table, the tobject. diapatch will continue to call the virtual method defaulthandler, tobject. defaulthandler is only a placeholder. This method is reloaded in twincontrol. The twincontrol inheritance class rarely reloads this method. It can be considered that the last chance of a message being processed is that it occurs in twincontrol. defaulthandler. We know that messages that are not processed in a message loop should be finally handed over to the default callback function defwindowproc API of windows for processing, twincontrol. the main task of defaulthandler is to complete this process. In addition, several additional messages are processed [2].
The message process of VCL ends now.
Maybe you are still confused about the entire message dispatching process. Let me use an instance for analysis.

3. Complete Message dispatching process of VCL
1. tbutton
Create an Application, put a Button on Form1 (the default name is Button1), write code in its OnClick event, and add a breakpoint. Before debugging, enable the DCU debugging switch (Project-> Options-> Compiler-> Use Debug DCUs). If this switch is not enabled, you cannot Debug VCL and run F9, when you stop at the breakpoint, open the Call Stack Window (View-> Debug Window-> Call Stack) and you can see that the Call sequence is as follows (from the bottom up ):

TForm1.Button1Click (37C0)
TControl. Click
TButton. Click
TButton. CNCommand (48401,660, 0, 524948, 0 ))
TControl. WndProc (48401,660,524 948, 0,660, 0,660, 8, 0, 0 ))
TWinControl. WndProc (48401,660,524 948, 0,660, 0,660, 8, 0, 0 ))
TButtonControl. WndProc (48401,660,524 948, 0,660, 0,660, 8, 0, 0 ))
TControl. Perform (48401,660,524 948)
DoControlMsg (524948, (no value ))
TWinControl. WMCommand (273,660, 0, 524948, 0 ))
TCustomForm. WMCommand (273,660, 0, 524948, 0 ))
TControl. WndProc (273,660,524 948, 0,660, 0,660, 8, 0, 0 ))
TWinControl. WndProc (273,660,524 948, 0,660, 0,660, 8, 0, 0 ))
TCustomForm. WndProc (273,660,524 948, 0,660, 0,660, 8, 0, 0 ))
TWinControl. MainWndProc (273,660,524 948, 0,660, 0,660, 8, 0, 0 ))
StdWndProc (918056,273,660,524 948)
TWinControl. DefaultHandler (no value ))
TControl. WMLButtonUp (514, 0, 48, 13, (48, 13), 0 ))
TControl. WndProc (514, 0, 852016, 0, 0, 0, 48, 13, 0, 0 ))
TWinControl. WndProc (514, 0, 852016, 0, 0, 0, 48, 13, 0, 0 ))
TButtonControl. WndProc (514, 0, 852016, 0, 0, 0, 48, 13, 0, 0 ))
TWinControl. MainWndProc (514, 0, 852016, 0, 0, 0, 48, 13, 0, 0 ))
StdWndProc (524948,514)
TApplication. HandleMessage
TApplication. Run
Project1

When a Button is clicked, two messages are generated in TButton: WM_LBUTTONDOWN/WM_LBUTTONUP. TButton does not process WM_LBUTTONUP (problem: Why only WM_LBUTTONUP is returned, these two messages should only occur in the Windows native control, unless TButton subclass has a "Button", I didn't see this part of the Code), just to TWinControl. defaultHandler, then TButton sends the generated WM_COMMAND message to its Parent, that is, TForm. After a series of messages are transmitted, WM_COMMAND is in TWinControl. WMCommand is processed, WM_COMMAND is processed into CN_COMMAND through DoControlMsg, and TControl is used. perform returns CN_COMMAND back to TButton In addition, a series of messages are passed to the Dispatch in TButton, And the Handler -- TButton is found through querying the dynamic method table. CNCommand, which calls the virtual method TButton. click, and then call TControl. click. In this method, FOnClick is called, the FOnClick feature value is the OnClick feature that Delphi automatically assigns to TButton when the programmer uses the object viewer to write the OnClick event processing function of TButton. In this example, OnClick is specified as TForm1.Button1Click, therefore, TForm1.Button1Click is finally called.

2. TForm
Create an Application and write a little code for the OnMouseDown event of Form1. Set a breakpoint on this method, run F9, and check the Call Stack.


TForm1.FormMouseDown (???,???, [SsLeft], 346,212)
TControl. MouseDown (mbLeft, [ssLeft], 346,212)
TControl. DoMouseDown (513, 1,346,212, (346,212), 0), mbLeft, [])
TControl. WMLButtonDown (513, 1,346,212, (346,212), 0 ))
TControl. WndProc (513, 1, 13893978, 0, 1, 0,346,212, 0, 0 ))
TWinControl. WndProc (513, 1, 13893978, 0, 1, 0,346,212, 0, 0 ))
TCustomForm. WndProc (513, 1, 13893978, 0, 1, 0,346,212, 0, 0 ))
TWinControl. MainWndProc (513, 1, 13893978, 0, 1, 0,346,212, 0, 0 ))
StdWndProc (2687598,513)
TApplication. HandleMessage
TApplication. Run
Project1

Click Form to generate two messages, WM_LBUTTONDOWN/WM_LBUTTONUP. However, we only intercept WM_LBUTTONDOWN. The generated WM_LBUTTONDOWN is delivered to TObject through a series of messages. dispatch: by querying the dynamic method table, Handler -- TControl is found in TControl, the parent class of TForm. WMLButtonDown, in TControl. WMLButtonDown goes through TControl again. doMouseDown, TControl. A series of MouseDown method calls are finally called to FOnMouseDown, and FOnMouseDown is assigned to TForm1.FormMouseDown. If you call FOnMouseDown, TForm1.FormMouseDown is called.
After talking about a lot of message implementation processes, what are the actual applications?

Iv. Actual Application of messages
If you are the author of shared software, you will often be troubled by the Crack of your software. What you can do is to enhance the Anti-Crack Function of your software. I will give you a try today.
If you have used DEDE, a special Disassembly tool for Delphi, you must know that the endpoint address of Event Handler methods such as Button1Click is very easy to locate, it is obtained based on the RTTI information of TForm (the address can be obtained by analyzing the dfm resource file). In fact, only the published class members in VCL form can generate RTTI information. You can prevent this from happening if you know this key point and have a deep understanding of the VCL message mechanism.
1. Anti-Crack
Create an Application and put two buttons on Form1, named btnRegister and btnCancel. Double-click these two buttons to generate two Event Handler skeleton codes: TForm1.btnCancelClick and TForm1.btnRegisterClick, then, cancel btnRegister in the object viewer. onClick is associated with TForm1.btnRegisterClick. Then, the TForm1.btnCancelClick statement is placed in the private section declared by TForms1. Add other parts according to the following code:


Unit Unit1;

Interface

Uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

Type
TForm1 = class (TForm)
BtnRegister: TButton;
BtnCancel: TButton;
Procedure btnCancelClick (Sender: TObject );
Private
Procedure btnRegisterClick (Sender: TObject );
Procedure WMCommand (var Message: TWMCommand); message WM_COMMAND;
Public
{Public declarations}
End;

Var
Form1: TForm1;

Implementation

{$ R *. dfm}

Procedure TForm1.btnCancelClick (Sender: TObject );
Begin
Close;
End;

Procedure TForm1.btnRegisterClick (Sender: TObject );
Begin
ShowMessage ('thx for ur registry .');
End;

Procedure TForm1.WMCommand (var Message: TWMCommand );
Begin
If Message. policycode = BN_CLICKED then
If FindControl (Message. Ctl) = btnRegister then
Begin
BtnRegisterClick (Self );
Exit;
End;
Inherited;
End;

End.

The essence of this method is to intercept the WM_COMMAND message of TForm1 and process it by yourself. Analyze the code by yourself. I will not talk about it much. After compilation, you can use DEDE disassembly to find out whether the entry address of TForm1.btnRegisterClick can be found easily.

Conclusion
Do you understand the VCL message mechanism? Is it particularly complicated? A message can be delivered to Event Handler only after more than 10 methods. Although it takes so long to transmit the message, the efficiency of the VCL message mechanism is still very high, because many of the key code is directly written in assembly, every Midway Station spends a very short time, so the messages to be processed can quickly reach the destination.
I started to learn Windows programming from the SDK. At that time, I wrote BASIC Windows programs. For a while, I always thought that SDK would be better than Delphi. Now I think it's silly, the VCL message mechanism is much more complex than the direct SDK programming. After reading the VCL source code, I feel like I have never learned programming before, but it is undeniable that, only after you have mastered the basic knowledge of OOP, ASM, and SDK, do you have the capital to understand the VCL source code?

References

1. Li Wei, chapter 4 and chapter 5 of in-depth core-VCL Architecture Analysis, 2004.1

2. savetime. "Introduction to the Message Mechanism of Delphi", Jan 2004

3. cheka. "Notes on VCL Window Function registration mechanism, compared with MFC", 200

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.