3
votes

Has anyone found a way to detect the mouse back and forward buttons in a Delphi FMX form in Windows (and only Windows)?

I understand this works fine in a VCL application, using

procedure WMAppCommand(var Msg: Winapi.Messages.TMessage); message WM_APPCOMMAND;

but this has no effect in an FMX application.

If anyone already has worked out a solution to this, would very much appreciate a hint (or the code they used, of course).

1
Are you sure your mouse is generating WM_AppComand's and not simply simulating Keyboard commands like VK_BROWSER_BACK and VK_BROWSER_FORWARD? If it is simulating key commands then all you need to do is use OnKeyDown or OnKeyUp events respectively. You would be surprised of how often these additional buttons on mice simply send specific keyboard commands to the computer.SilverWarior
@SilverWarior Yes, I am. The OnKeyDown and OnKeyUp events were among the first things I checked. They are not triggered.Domus

1 Answers

4
votes

FMX heavily filters window messages, only dispatching the few messages it actually uses for itself. WM_APPCOMMAND is not one of them, which is why a simple message handler does not work in FMX, like it does in VCL.

So, you are going to have to manually subclass the TForm's Win32 HWND directly, via SetWindowLongPtr(GWLP_WNDPROC) or SetWindowSubclass(), in order to intercept window messages before FMX sees them. See Subclassing controls.

An ideal place to do that subclassing is to override the TForm.CreateHandle() method. You can use FMX's FormToHWND() function to get the TForm's HWND after it has been created.

protected
  procedure CreateHandle; override;

...

uses
  FMX.Platform.Win, Winapi.Windows, Winapi.CommCtrl;

function MySubclassProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM;
  uIdSubclass: UINT_PTR; dwRefData: DWORD_PTR): LRESULT; stdcall;
begin
  case uMsg of
    WM_APPCOMMAND: begin
     // use TMyForm(dwRefData) as needed...
    end;
    WM_NCDESTROY:
      RemoveWindowSubclass(hWnd, @MySubclassProc, uIdSubclass);
  end;
  Result := DefSubclassProc(hWnd, uMsg, wParam, lParam);
end;

procedure TMyForm.CreateHandle;
begin
  inherited;
  SetWindowSubclass(FormToHWND(Self), @MySubclassProc, 1, DWORD_PTR(Self));
end;

procedure InitStandardClasses;
var 
  ICC: TInitCommonControlsEx;
begin
  ICC.dwSize := SizeOf(TInitCommonControlsEx);
  ICC.dwICC := ICC_STANDARD_CLASSES;
  InitCommonControlsEx(ICC);
end;

initialization
  InitStandardClasses;