2

I would like to create a DLL which, when loaded into a process, adds an "Always on top" item to the applications Sysmenu. I created a hook trying both SetWindowsHookEx(WH_CALLWNDPROC,...) and SetWindowsHookEx(WM_GETMESSAGE,...), and from there add the menu and check the WM_ messages for click events. WM_SYSCOMMAND is triggered, but without my inserted menu items ID (ALWAYS_ONTOP). Why is my menu item not being processed (or, why can't I capture my inserted menu items click events)?

library dlltest;
uses
  Windows, messages, sysutils;

var
  Hook    : HHOOK;
  Enabled : boolean;
Const
  ALWAYS_ONTOP = 100;

Function  GetMessageCallback(code: integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
Var
  uMsg  : TMSG;
  hMenu : Thandle;
Begin
  IF(Code = HC_ACTION)Then
  Begin
//--
//-- Insert Menu Item
    IF (Enabled = False) then
    Begin
      hMenu := GetSystemMenu(umsg.hwnd, FALSE);
      IF (hMenu <> 0) Then
      Begin
       {Enabled :=}AppendMenu(hMenu, MF_SEPARATOR, 0, '');
        Enabled := AppendMenu(hMenu, MF_STRING, ALWAYS_ONTOP, 'Always On Top');
      End //Else writetext('hMenu=0!');
    End;
//--
//-- Process message(s)
    uMsg := PMSG(lParam)^;
    case (uMsg.message) of

      WM_SYSCOMMAND:
        Begin
          case loword(uMsg.message) of
            ALWAYS_ONTOP:
              Begin
                //toggle checkmark
                MessageBoxA(0, 'Hello World', 'test', 0);
              End;
          End;//Case
        End;//WM_SYSCOMMAND:

      WM_INITMENU:
        Begin
          //GetSystemMenu returns 0?, hmm
          //writetext('WM_INITMENU');
        End;//WM_INITMENU:

    end;//case uMsg
  end;//IF (HC_ACTION)
  Result := CallNextHookEx(Hook, Code, wParam, lParam);
End;

begin
  enabled := False;
  Hook    := SetWindowsHookEx(WH_GETMESSAGE{WH_CALLWNDPROC}, @GetMessageCallback {@WndProcCallback}, 0, GetCurrentThreadId());
  IF(Hook = 0)Then
  Begin
    messagebox(0, 'no hook', 'fail', 0);
  End;
end.

sidenote: my WndProcCallback() was basically the same as GetMessageCallback(), except that I cast lParam to a PCWPStruct, as per MSDN.

Ken White
  • 123,280
  • 14
  • 225
  • 444
cracksman
  • 61
  • 5
  • umm, just realised my title is misleading, how do I change it? – cracksman Feb 28 '15 at 21:13
  • Read the help http://stackoverflow.com/help/editing – Sir Rufo Feb 28 '15 at 22:15
  • Click the [edit] link below the tags (in this case, just between `delphi` and `hook`).. Please only use tags that are applicable to your question. Delphi is not Pascal. The generic Pascal tag is for the traditional Pascal language. Delphi may have originated from Turbo Pascal, but it has evolved into it's own language. Thanks. – Ken White Feb 28 '15 at 22:24
  • 1
    Why are you using `loword(uMsg.message)` when testing for `ALWAYS_ONTOP`? By that point, you already know `uMsg.message` is `WM_SYSCOMMAND` by itself. It does not contain the `ALWAYS_ONTOP` value within its bits. You should be testing for `uMsg.wParam and $FFF0` instead. That being said, have you considered using a `WH_CBT` hook yet? Try adding your menu item in the `HCBT_CREATEWND`, `HCBT_ACTIVATE` or `HCBT_SETFOCUS` notification, and process the menu item click in the `HCBT_SYSCOMMAND` notification. – Remy Lebeau Mar 01 '15 at 01:08
  • @RemyLebeau yea `uMsg.wParam and $FFF0` seems to have fixed it. I also(just now) tried setting a hook with `WH_CBT`. `HCBT_ACTIVATE` or `HCBT_SETFOCUS` re-add the menu with every active/focus (obviously), so I would perfer to use `HCBT_CREATEWND`; However, GetSystemMenu() will return 0 there, (I assume the menu has not been created at this point?) so how would I add it there?. moving along, when my menu is clicked wParam in `HCBT_SYSCOMMAND` is returning $96, rather than `ALWAYS_ONTOP` ($100), why is that? – cracksman Mar 01 '15 at 19:36
  • 1
    `WM_SYSCOMMAND` reserves the low 4 bits of `wParam` for internal use (which is why you have to use `wParam and $FFF0` when testing the value). 100 in binary is `01100100`, and 96 in binary is `01100000`. Look familiar? 96 is 100 with its low 4 bits set to 0 (in this case, 100-4=96). So 100 as a menu item ID is not compatible with `WM_SYSCOMMAND`. Use a different value that does not use the low 4 bits, or conflict with the existing `SC_...` defined IDs. – Remy Lebeau Mar 01 '15 at 20:28

0 Answers0