Solved

Tmessage

Posted on 2002-03-07
11
696 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
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
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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
 
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

Ready to get started with anonymous questions?

It's easy! Check out this step-by-step guide for asking an anonymous question on Experts Exchange.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
In this video, viewers will be given step by step instructions on adjusting mouse, pointer and cursor visibility in Microsoft Windows 10. The video seeks to educate those who are struggling with the new Windows 10 Graphical User Interface. Change Cu…
This tutorial will teach you the special effect of super speed similar to the fictional character Wally West aka "The Flash" After Shake : http://www.videocopilot.net/presets/after_shake/ All lightning effects with instructions : http://www.mediaf…
Suggested Courses
Course of the Month9 days, 23 hours left to enroll

624 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