Solved

Tmessage

Posted on 2002-03-07
11
644 Views
Last Modified: 2010-04-05
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm1 = class(TForm)
  private
    { Private declarations }
    procedure WMSysCommand(var Msg : TWMSysCommand);
         message WM_SYSCOMMAND;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


 procedure TForm1.WMSysCommand(var Msg : TWMSysCommand);
 begin
   //trap the message and set its result to -1
   if (Msg.CmdType = SC_SCREENSAVE) then
     Msg.Result := -1
   else
     inherited;
 end;

end.


I am trying to understand what is happening here, so what i did was copied it  replaced TWMsysCommand with TMessage

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm1 = class(TForm)
  private
    { Private declarations }
    procedure WMSysCommand(var Msg : TMessage);
       message WM_SYSCOMMAND;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


 procedure TForm1.WMSysCommand(var Msg : TMessage);
 begin
   //trap the message and set its result to -1
   if (Msg.WParam = SC_SCREENSAVE) then
     Msg.Result := -1
   else
     inherited;
 end;

end.

and no supprise really, it never stopped the screensave, how on earth do you figure out what Message to use to get what you want? - could someone explain to me why that does not work?

- Thanks

Craig C.
0
Comment
Question by:craig_capel
11 Comments
 
LVL 3

Expert Comment

by:raidos
ID: 6847562
hmm...

Is there any reason why you need to wait until the screensaver is about to start and then decide not to let it start ?

otherwise you could disable the screensaver upon program start and restore it on program end with a call to the SystemParametersInfo function.

Regards
//raidos
0
 
LVL 3

Expert Comment

by:SteveWaite
ID: 6847596
Maybe it's something like:
Windows expects to place information for the message in a pre defined data structure (TWMSysCommand). TMessage is smaller, perhaps no information is placed in it.
(listening)

0
 
LVL 2

Author Comment

by:craig_capel
ID: 6847600
sorry, i was not very clear. The first section of code works fine, i want to learn more about how Tmessage works, for example, why in Some example....


   private
                         FForm : TForm;
                         procedure doWMNCHITTEST(var msg : TWMNCHITTEST);message WM_NCHITTEST;
                         procedure doLBUTTONUP(var msg : TWMLBUTTONUP);message WM_LBUTTONUP;

How do you know what goes in the brackets ( Msg: ???? )

procedure WMSysCommand(var Msg : TWMSysCommand);
                             message WM_SYSCOMMAND;

how and why use " Msg : TWMSysCommand" to get the screen saver?

what happens if i replaced all of them with Msg: Tmessages;  would it still work, basically HOW does this work? every example i see it's different.


                     type
                      TForm1 = class(TForm)
                      private
                        { private declarations }
                        procedure WMDeviceChange(var Msg: TMessage); message WM_DeviceChange;
                      public
                        { public declarations }
                      end;


                     procedure TForm1.WMDeviceChange(var Msg: TMessage);
                     const
                      DBT_QUERYCHANGECONFIG = $0017;
                      DBT_CONFIGCHANGED = $0018;
                      DBT_CONFIGCHANGECANCELED = $0019;
                      DBT_DEVICEARRIVAL = $8000;
                      DBT_DEVICEQUERYREMOVE = $8001;
                      DBT_DEVICEQUERYREMOVEFAILED = $8002;
                      DBT_DEVICEREMOVEPENDING = $8003;
                      DBT_DEVICEREMOVECOMPLETE = $8004;
                      DBT_DEVICETYPESPECIFIC = $8005;
                      DBT_USERDEFINED = $FFFF;
                     var
                      tmpStr : String;
                     begin
                      inherited
                      case Msg.wParam of
                        DBT_DEVICEARRIVAL:
                          tmpStr := 'CD inserted in drive';
                        DBT_DEVICEREMOVECOMPLETE:
                          tmpSTr := 'CD removed from drive';
                      end;
                      ShowMessage(tmpStr);
                     end;

And this example it uses  procedure WMDeviceChange(var Msg: TMessage); message WM_DeviceChange;  TMessage?

it's very confusing to me, why does that example not use TMMediaMadeUp Messages, how do you know what to use.

So i for example wanted to make a hook to do something simple, how do i know where to start looking if Tmessages is not to be used? when someone worked out the screen saver, why not always use Tmessages, like the last example, why and where did he she get the information for
procedure WMSysCommand(var Msg : TWMSysCommand);
                             message WM_SYSCOMMAND;

Basically so many hooks and messages - where does someone like me with little experience start to learn?

- Thanks

Craig C.
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 6847805
type
  TMessage = packed record
    Msg: Cardinal;
    case Integer of
      0: (
        WParam: Longint;
        LParam: Longint;
        Result: Longint);
      1: (
        WParamLo: Word;
        WParamHi: Word;
        LParamLo: Word;
        LParamHi: Word;
        ResultLo: Word;
        ResultHi: Word);
  end;

type
  TWMSysCommand = packed record
    Msg: Cardinal;
    case CmdType: Longint of
      SC_HOTKEY: (
        ActivateWnd: HWND);
      SC_KEYMENU: (
        Key: Word);
      SC_CLOSE, SC_HSCROLL, SC_MAXIMIZE, SC_MINIMIZE, SC_MOUSEMENU, SC_MOVE,
      SC_NEXTWINDOW, SC_PREVWINDOW, SC_RESTORE, SC_SCREENSAVE, SC_SIZE,
      SC_TASKLIST, SC_VSCROLL: (
        XPos: Smallint;
        YPos: Smallint;
        Result: Longint);
  end;

As you can see, both types have the same size. Both have a field Msg of type cardinal. But while TMessage has a WParam as LongInt value, TWMSysCommand uses CmdType for the same memory space. It's just the WParam member but with a different name to make the code a bit more clear.
They are both variant records.
TMessage then has 2 other fields of 4 bytes each. The TWMSysCommand however has an other name for the memory parts in this message. The LParam of TMessage is divided into XPos and YPos if the CmdType is SC_SCREENSAVE. In that case you also have the result field.

Actually, there are not hundreds of types of TMessage records. There is only one type: TMessage and several other types that can be typecasted as TMessage. You might want to use these typecasts to make these messages more readable. But all this is about is about a piece of memory of 16 bytes where the first 4 define the message type and the last 4 define the result code. The 8 bytes in-between are for the user to define.
It is a bit confusing because for TMessage-like record-types Borland uses quite a lot of variant records. What this means is that parts of this block of memory can be accessed using different names.
0
 
LVL 8

Accepted Solution

by:
TOndrej earned 100 total points
ID: 6848554
A Delphi message handler method must follow the following syntax:

procedure <proc_name>(var <param>[: <type_spec>]); message <msg_id>; of object;

where <param> must be passed by reference (var) and can be of any type or untyped (ie. <type_spec> can be ommitted>).
<msg_id> must be an integer value, usually constant.
'of object' means it must be a method of a class; not a standalone procedure.

For example:

const
  WM_MOUSEPADMOVE = WM_USER + $100;
  WM_MOUSESQUEAK = WM_USER + $101;

type
  TMyClass = class
  private
    procedure WMMousePadMove(var Message: TMessage); message WM_MOUSEPADMOVE;
    procedure WMMouseSqueak(var M); message WM_MOUSESQUEAK;
  end;

'message' directive marks the procedure as a special type of dynamic method (stored in the class DMT - Dynamic Method Table which is an internal array of method pointers generated by the compiler).
This special type of method is however different from virtual or dynamic method; its only unique identifier is its <msg_id> number.

Message handlers are processed at runtime by TObject.Dispatch method which is declared like this:

procedure TObject.Dispatch(var Message);

It takes whatever is passed to it, treats the first four bytes of it as the message ID, looks up the corresponding method in the object's DMT, and calls it.

Message handlers, although they are dispatched in the same way as dynamic methods, are not polymorphic - they are compiled into static calls to the methods as defined in the class with which the variable is declared, not the actual class of the actual instance in the variable.

You can call inherited; from within a message handler and may not specify a method name or parameter; the nearest ancestor's message handler declared with the same message ID will be called even if it was declared private, with a different name or different parameter.

A bare-bones Windows API window procedure (declared in C) looks like this:

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

where

hwnd is the window handle (integer),
uMsg is message identifier (integer)
wParam is first parameter (integer)
LParam is second parameter (integer).
return value is also integer.

What Delphi does very nicely is it maps this window procedure into the VCL class hierarchy. The TMessage type is declared in Messages.pas like this (if you ignore the variant record for a while):

type
  TMessage = packed record
    Msg: Cardinal; // offset 0, size 4
    WParam: Longint; // offset 4, size 4
    LParam: Longint; // offset 8, size 4
    Result: Longint; // offset 12, size 4
  end;

which together with a window handle (TWinControl.Handle) is all that's needed to process a Windows message.

There are many different Windows messages which are used to send many kinds of different information to windows.
For example, WM_MOUSEMOVE is defined as follows:

WM_MOUSEMOVE  
fwKeys = wParam; // key flags
xPos = LOWORD(lParam); // horizontal position of cursor
yPos = HIWORD(lParam); // vertical position of cursor

If you write a TWinControl descendant which needs to process this message you could do something like this:

procedure TMyClass.MyMouseMoveProc(var Message: TMessage);
var
  Keys: Longint;
  X, Y: Smallint;
begin
  Keys := Message.WParam;
  X := HiWord(Message.LParam);
  Y := Word(Message.LParam);
  // do something based on Keys, X and Y
end;

but a better solution is to declare a new record type to reflect this message's specific parameter types, e.g.:

type
  TWMMouse = packed record
    Msg: Cardinal; // offset 0, size 4
    Keys: Longint; // offset 4, size 4
    XPos: Smallint; // offset 8, size 2
    YPos: Smallint; // offset 10, size 2
    Result: Longint; // offset 12, size 4
  end;

which is just a different representation of the same piece of data in memory. You can then declare your message handler with this type:

procedure TMyClass.MyMouseMoveProc(var Message: TWMMouse);
var
  ShiftState: TShiftState;
begin
  ShiftState := KeysToShiftState(Message.Keys);
  if (ssCtrl in ShiftState) and (X > 100) and (Y > 200) then
  ...
end;

This is possible because the message handler parameter type is ignored, remember?
This way you can write more readable code, IMHO. You can find TWMKey and other 'message cracker' types in Messages.pas unit, and you can also declare your own.

Now for the variant record types:

Imagine you want to access the data stored in X, Y members as TSmallPoint directly (to pass it to another routine, perhaps). It could be done by using a local TSmallPoint variable but you want your code to be as straightforward as possible, so you go ahead and declare your type like this:

type
  TWMMouse = packed record
    Msg: Cardinal; // offset 0, size 4
    Keys: Longint; // offset 4, size 4
    Pos: TSmallPoint; // offset 8, size 4
    Result: Longint; // offset 12, size 4
  end;

and your method like this:

procedure TMyClass.MyMouseMoveProc(var Message: TWMMouse);
var
  ShiftState: TShiftState;
begin
  ShiftState := KeysToShiftState(Message.Keys);
  if (ssCtrl in ShiftState) and PtInRect(ClientRect, SmallPointToPoint(Message.Pos)) then
    ...
end;

If you look at the declaration of TWMMouse in Messages.pas, you will find this:

type
  TWMMouse = packed record
    Msg: Cardinal; // offset 0, size 4
    Keys: Longint; // offset 4, size 4
    case Integer of
      0: (
        XPos: Smallint; // offset 8, size 2
        YPos: Smallint); // offset 10, size 2
      1: (
        Pos: TSmallPoint; // offset 8, size 4
        Result: Longint); // offset 12, size 4
  end;

which is simply a combination of both: with a variable M of type TWMMouse you can reference M.XPos and M.YPos or M.Pos as you like, without having to declare and assign any local variables.

Finally, your message handler method may be called in one of the following ways:

1. By sending the message to the window handle - the message can come from Windows or from another piece of code, e.g.
  SendMessage(MyControl1.Handle, WM_MOUSEMOVE, 0, 0);
or
  PostMessage(MyControl1.Handle, WM_MOUSEMOVE, 0, 0);

2. By calling Perform, e.g.
  MyControl1.Perform(WM_MOUSEMOVE, 0, 0);
In this case, the handler procedure is performed directly, bypassing the window's message queue.

3. By calling it directly:
  MyControl1.WMMouse(Message);
but that's not clean design, and I don't recommend this way. Normally, message handlers are declared as private so you cannot do it from another unit anyway.

HTH
TOndrej
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 17

Expert Comment

by:inthe
ID: 6848774
nice explanation TOndrej :)
0
 
LVL 8

Expert Comment

by:TOndrej
ID: 6848816
Thanks, inthe :-)
0
 
LVL 8

Expert Comment

by:TOndrej
ID: 6848855
Oh yes, I've just found a few mistakes in the code snippets I had posted (typed directly into browser), so please take them only as hints, not literally.
0
 
LVL 2

Author Comment

by:craig_capel
ID: 6849434
Yup very nice - Thanks :) what the heck since you spent time thinking i will give u an extra 50 points :)
0
 
LVL 2

Author Comment

by:craig_capel
ID: 6849560
Yup very nice - Thanks :) what the heck since you spent time thinking i will give u an extra 50 points :)
0
 
LVL 8

Expert Comment

by:TOndrej
ID: 6849996
thanks :-)
0

Featured Post

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now