Search code examples
windowsdelphimenuwindowwindows-messages

Delphi: custom window menu: Msg.CmdType and $FFF0 clarification needed


I am doing some finishing touches on my Windows application developed in Delphi XE6.

Currently, I become confused about window system menu, I mean the menu that appears when you click onto the icon in the title bar on the left.

I have defined two procedures:

// this inserts one additional command into the menu
procedure InsertCommand(Sender: TObject);

// this is obviously a handler of mouse clicks on that menu
procedure OnSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;

The definitions follow:

const
  ALWAYS_ON_TOP_ID = 1000;

var
  HandleMenu: THandle;
  sAlwaysOnTop: string;

procedure TFormMain.InsertCommand(Sender: TObject);
begin
  // get the system menu handle and store it into form-level defined variable
  HandleMenu := GetSystemMenu(Handle, False);

  // just to make it nicer, I add a separator line
  AppendMenu(HandleMenu, MF_SEPARATOR, 0, '');

  // append "Always on Top" language localized string at the end of the menu
  AppendMenu(HandleMenu, MF_STRING, ALWAYS_ON_TOP_ID, PChar(sAlwaysOnTop));
end;


procedure TFormMain.OnSysCommand(var Msg: TWMSysCommand);
begin

  if Msg.CmdType = ALWAYS_ON_TOP_ID then
    if GetMenuState(HandleMenu, ALWAYS_ON_TOP_ID, MF_BYCOMMAND) and MF_CHECKED = MF_CHECKED
      then begin
        FormStyle := fsNormal;
        CheckMenuItem(HandleMenu, ALWAYS_ON_TOP_ID, MF_UNCHECKED);
      end else begin
        FormStyle := fsStayOnTop;
        CheckMenuItem(HandleMenu, ALWAYS_ON_TOP_ID, MF_CHECKED);
      end;

  inherited;
end;

I now read that the right usage is:

Msg.CmdType and $FFF0

but if I use that, my code stops working.

From the official MSDN source, I quote:

In WM_SYSCOMMAND messages, the four low-order bits of the wParam parameter are used internally by the system. To obtain the correct result when testing the value of wParam, an application must combine the value 0xFFF0 with the wParam value by using the bitwise AND operator.

Question is, do I have to avoid the $FFF0 mask, or is there a more proper way?


Solution

  • The documentation is accurate, the reason your code stops working when you test bitwise AND with $FFF0 is the constant you define is no good.

    const
      ALWAYS_ON_TOP_ID = 1000;
    

    1000 in hexadecimal is 3E8, the lowest order hexadecimal digit should be 0 for the lowest four bits to be 0. IOW, binary 1111 is hexadecimal F, so you should leave the last digit to the system.

    Set your constant in hexadecimal so you won't make a mistake. Just be careful to stay clear off the SC_... range (which is $F000 ... $F###) when defining your constant. E.g.:

    const
      ALWAYS_ON_TOP_ID = $100; {256}
    

    so now you can safely test

      if Msg.CmdType and $FFF0 = ALWAYS_ON_TOP_ID then
        ...