• 0
Просто Проги

[Windows] Глобальный хук на клавиши возможно ли FMX

Вопросы

Собственно сделал я  программу мини проигрователь всё вроде даже работает, но нужно переключать треки когда приложение свернуто,т.е не видно пробовал реализацию через WinApi

var
  FWnd:HWnd;

RegisterHotKey(FWnd, Ord('U'), 0, Ord('U'));

но почему то нечего не работает(

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

30 ответов на этот вопрос

  • 0

то же самое ошибки и ничего не происходит uses уже был прописан до этого попробовал на чистом приложении результат тот же.

ошибка такого содержания  Incompatible types: 'HWND' and 'TWinWindowHandle'

 

код.rar

Изменено пользователем Просто Проги

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
  FWnd := WindowHandleToPlatform(Self.Handle).Wnd;

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Ошибок нет, но и события не выполняется пробую вызывать так:

procedure TForm1.OnHotKey(var Msg: TWMHotKey);
begin
ShowMessage('1');
end;

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Потому что в FMX никакие виндовые сообщения до формы не доходят. Так как это кроссплатформенный фреймворк, где сообщения есть только на винде.

Единственный вариант, если вы хотите так делать, в разрез текущей ситуации, это можно прокинуть отправку сообщения форме. через вызов метода Form.Dispatch

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Если есть более простой способ подскажите как сделать горячие клавиши а то уже всю голову сломал(

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Так, вроде, сам TActionList поддерживает хот кеи. Сделай свой экшен и задайте ему hot key.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Это я делал но если форма не видна то работать не будет, а мне нужно что бы при скрытой форме данное событие происходило, т.е если я скрыл проирователь но моу переключать песни через свою клавишу к примеру i

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

кстате забавная ситуация

 RegisterHotKey(FWnd, Ord('U'), 0, Ord('U'));

сейчас попробовал написать в браузере букву u но не получается значит клавиши назначилась осталось как то её передать форме возможно метод Form.Dispatch поможет можно поподробнее как этот метод использовать первый раз про такой метод слышу.(

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Вот, посмотрите мой ответ в другой теме: Обработка WM_GETMINMAXINFO

Там же есть вариант с хуком и подменой оконной процедуры.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Спасибо,буду пробовать)

вообщем сделал всё как по примеру компилится ошибок нет но и события не происходит опять что ли handle не находит? код выглядит вот так

походу опять что то накосячил
 

Спойлер

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  {$IF Defined(WIN32) OR Defined(WIN64)}
  System.Generics.Collections, Winapi.Windows, FMX.Platform.Win, Winapi.Messages,
  {$ENDIF}
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  {$IF Defined(WIN32) OR Defined(WIN64)}
  private
   class var Callbacks: TDictionary<HWND, Pointer>;
   class constructor ClassCreate;
   class destructor ClassDestroy;
    procedure OnHotKey(var Msg: TWMHotKey); message WM_HOTKEY;
 protected
    procedure CreateHandle; override;
    procedure DestroyHandle; override;
  {$ENDIF}
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}
{$IF Defined(WIN32) OR Defined(WIN64)}

{ TfrmPersistent }
function WindowProc(hwnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
  frm: TCommonCustomForm;
  oldProc: Pointer;
begin
  frm := FindWindow(hwnd);
//Здесь я так понимаю происходит событие присвоения и проверка uMsg на параметр WM_GETMINMAXINFO,возможно ли здесь так же передавать кнопку горячую или сочитания ? 
  if Assigned(frm) and (uMsg = WM_GETMINMAXINFO) then
  begin
    with PMinMaxInfo(LParam)^.ptMinTrackSize, frm do
    begin
      X := 950 + Width - ClientWidth;
      Y := 500 + Height - ClientHeight;
    end;

    Result := 0;
  end else

  if TForm1.Callbacks.TryGetValue(hwnd, oldProc) and Assigned(oldProc) then
    Result := CallWindowProc(oldProc, hwnd, uMsg, wParam, lParam)
  else
    Result := DefWindowProc(hwnd, uMsg, wParam, lParam);
end;
procedure TForm1.CreateHandle;
var
  wnd: HWND;
begin
  inherited CreateHandle;

  wnd := FormToHWND(Self);

  if wnd <> 0 then
  begin
    Callbacks.Add(wnd, Ptr(GetWindowLongPtr(wnd, GWLP_WNDPROC)));

    SetWindowLongPtr(wnd, GWLP_WNDPROC, NativeInt(@WindowProc));
  end;
end;
procedure TForm1.DestroyHandle;
var
  wnd: HWND;
  oldProc: Pointer;
begin
  wnd := FormToHWND(Self);

  if (wnd <> 0) and Callbacks.TryGetValue(wnd, oldProc) then
    SetWindowLongPtr(wnd, GWLP_WNDPROC, NativeInt(oldProc));

  inherited DestroyHandle;
end;

class constructor TForm1.ClassCreate;
begin
  Callbacks := TDictionary<HWND, Pointer>.Create;
end;

class destructor TForm1.ClassDestroy;
begin
  Callbacks.Free;
end;
{$ENDIF}
procedure TForm1.OnHotKey(var Msg: TWMHotKey);
begin
   inherited;
 ShowMessage('stop');
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
// RegisterHotKey(hwnd, Ord('U'), 0, Ord('U')); //гор. клавишиа U
end;

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Изменить нужно

WM_GETMINMAXINFO

На 

WM_HOTKEY

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Это тоже пробовал результата 0 компилится но события так и нет(

Всё спасибо всем всё заработало))

Походу рано радовался на одно событие работает, а второе нет как можно проверить что именно данный hot key был нажат
 

 if Assigned(frm) and (uMsg = WM_HOTKEY) then
  begin

ShowMessage('событие след трек');
  end;

задаю hot следующим методом

RegisterHotKey(WindowHandleToPlatform(Self.Handle).Wnd, Ord('U'), 0, Ord('U')); //регистрац. гор. клавиши U

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

первый Ord('U') это Id по нему можно ориентироваться

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

а куда можно подставить проверку пробовал проверять так

if uMsg=Ord('U') then

но резултата это не принесло(

тк  umsg =WM_HOTKEY

Изменено пользователем Просто Проги

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 2

Легче всего, по моему мнению, сделать так:

...
  FWnd := AllocateHWnd(WindowProc);
...

procedure WindowProc(var AMsg: TMessage);
begin
  case AMsg.Msg of
    WM_HOTKEY:
      case TWMHotKey(AMsg).hotkey of
        1: ...;
        2: ...;
        ...
      end;
  end;
end;

...
RegisterHotkey(FWnd, ...); // как обычно в VCL
...
UnregisterHotkey(...); // как обычно в VCL
...

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Сделал на чистом примере  так, но нечего не происходит замкнутый круг походу:blink:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,Winapi.Messages,FMX.Platform.Win,Winapi.Windows;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
  procedure WindowProc(var AMsg: TMessage);
    { Public declarations }
  end;

var
  Form1: TForm1;
  FWnd:HWND;
implementation

{$R *.fmx}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
//FWnd := WindowHandleToPlatform(Self.Handle).Wnd;
FWnd := AllocateHWnd(WindowProc);
RegisterHotKey(FWnd, Ord('U'), 0, Ord('U'));
end;

procedure TForm1.WindowProc(var AMsg: TMessage);
begin
case AMsg.Msg of
    WM_HOTKEY:
      case TWMHotKey(AMsg).hotkey of
        1: ShowMessage('Событие первого hotkey');
        2: ShowMessage('Событие первого hotkey');
      end;
  end;
end;

end.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Извините, но Вы хоть сами поняли, что написали выше? :-)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
13 минут назад, Brovin Yaroslav сказал:

Извините, но Вы хоть сами поняли, что написали выше? :-)

Недо поспать чет я совсем уже запутался)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Так, то что в VCL вы привыкли писать так:

procedure OnHotKey(var Msg: TWMHotKey); message WM_HOTKEY;

Это заслуга Delphi TObject.Dispatch. Суть простая, вызвав Dispatch и передав внутрь сообщение TMessage с ID, TObject автоматически найдет среди ваших методов метод, у которого WM_HOTKEY будет равен ID сообщения. Таким образом можно передавать любые варианты сообщений. Главное, чтобы первые 4 байта - это были ID сообщения. 

В VCL оконная процедура просто вызывает у формы Form.Dispatch и передает туда виндовое сообщение. Так все и работает.

В FMX другая идеология, она не редиректит сообщения из оконной процедуры в форму. Поэтому вам предложили сделать следующее:

  1. Или повесить хук на оконную процедуру и обрабатывать свои сообщения там.
  2. Либо сделать редирект сообщения, как я указал на уровне платформы.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

 

В 10/18/2016 в 18:54, Brovin Yaroslav сказал:
  • Или повесить хук на оконную процедуру и обрабатывать свои сообщения там.
  • Либо сделать редирект сообщения, как я указал на уровне платформы.

Есть еще вариант - Заменить оконную процедуру на свою.

Второй вариант с модификацией юнита FMX.Platform.Win я бы вообще исключил, т.к. модуль поменяться в будущем и придут новые проблемы.

Вот есть вариант с хуком:

http://teran.karelia.pro/articles/item_6148.html

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
6 часов назад, ENRGY сказал:

Заменить оконную процедуру на свою.

Это и называется хук

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
1 час назад, Brovin Yaroslav сказал:

Это и называется хук

Насколько я знаю хук устанавливается при помощи SetWindowsHookEx. 

А оконная процедура (WndProc ) переопределяется в своем процессе при помощи SetWindowLong и GWL_WNDPROC без каких то плясок.

Изменено пользователем ENRGY

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
16 минут назад, ENRGY сказал:

Насколько я знаю хук устанавливается при помощи SetWindowsHookEx. 

А оконная процедура (WndProc ) переопределяется в своем процессе при помощи SetWindowLong и GWL_WNDPROC без каких то плясок.

Простая замена не поможет. Так как придется попотеть и заново восстановить всю логику работы платформы fmx для винды. Иначе ни один контрол работать и рисоваться не будет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти


  • Похожий контент

    • От Вадим Смоленский
      В своем проекте (это японско-русский словарь) мне приходится выводить на TImage.Bitmap.Canvas хранящиеся в юникоде иероглифы, причем шрифт пользователь может выбрать сам. Столкнулся с неприятным явлением: разные шрифты располагают выводимый символ на разной высоте, в результате чего иероглиф часто не вписывается в предназначенную для него область. По умолчанию принят шрифт Tahoma, с ним всё хорошо:

      Но вот шрифт Yu Mincho:

      А вот, еще хлеще, Kozuka Gothic:

      Вопрос: что за параметр регулирует вертикальное смещение, можно ли его вынуть и с ним работать?
    • От Вадим Смоленский
      Некоторые юзеры (видимо, из близоруких) прибегают к масштабированию экрана и требуют, чтобы ваше приложение тоже масштабировалось. Когда мой проект был в VCL, я смотрел на параметр Screen.PixelsPerInch и учитывал его значение в своем коде. В FireMonkey этого нет. Я пытался понять, как это делать теперь, и окончательно запутался. Microsoft на официальном сайте упоминает функцию SystemParametersInfo с параметром SPI_GETLOGICALDPIOVERRIDE, но делает оговорку, что такую конфигурацию лучше не использовать - дескать, в новых версиях Windows она не будет работать. Взамен рекомендуют функцию GetScaleFactorForDevice, но тут же заявляют, что под Windows 8 и она работать не будет, а будет работать только GetScaleFactorForMonitor. Голова кругом. Нет ли у кого, случаем, готового решения, которое работало бы на любых Windows - хотя бы от семерки до десятки?
      Еще один момент, который я не могу понять - в Windows 10 в настройках экрана, помимо масштаба в процентах, можно выбирать еще и разрешение в пикселях. По-моему, раньше такого разделения не было, хотя могу ошибаться. Связаны ли эти настройки друг с другом? И как они соотносятся со старым добрым DPI?
    • От Вадим Смоленский
      Работая в Berlin, подправил стиль для одного компонента посредством вызова "Edit Custom Style" во всплывающем меню. Потом перешел на Tokyo; потом поменял компьютер и установил на него Tokyo заново. Сейчас вижу, что компонент отображается в стиле по умолчанию. Никаких сообщений по поводу пропавшего стиля не припомню. Где мне найти и как опять подключить мой отредактированный стиль?
    • От Вадим Смоленский
      В своем проекте (это японско-русский словарь) я часто вывожу текст на TImage методом TCanvas.FillText, и результат выглядит так:

      Один из тестеров, у которого установлена Windows XP, прислал скриншот того, что выводится у него:

      Ровно такую же безобразную картину я видел у себя, когда, экспериментируя с быстродействием, вставлял в файл dpr операторы  FMX.Types.GlobalUseDX:=False или FMX.Types.GlobalUseDirect2D:=False
      То ли в XP эти установки приняты по умолчанию, то ли так сконфигурирована система у тестера. Первое, что приходит в голову - вставить в код присвоение обеим упомянутым переменным значения True. Но прежде, чем предпринять такую попытку, хотелось бы услышать мнение экспертов. В чем здесь корень зла?
    • От Вадим Смоленский
      Записываю русский текст в файл:
      procedure WriteToFile; var F: TextFile; S: string; begin S:='Русский текст'; AssignFile(F,MyFile.txt'); ReWrite(F); WriteLn(F,S); CloseFile(F); end; Тип данных string - по идее, то же, что WideString. То бишь, в строку S записан юникод. Тем не менее, открывая потом файл, вижу, что русский текст сохранен в ANSI. Как добиться его сохранения в юникоде?
    • От Вадим Смоленский
      Писал на Delphi 10.2 Tokyo под Windows. У тестеров приложение валится при первой же попытке обращения к БД с сообщением "unable to open database file". У меня на компе всё нормально. Файл sqlite3.dll версии 3.21.0.0 в дистрибутив включен, кладется рядом с исполнимым файлом. Что может быть не так?
    • От Вадим Смоленский
      В своем VCL-проекте я использовал оригинальный способ застраховаться от повторного запуска приложения. Обычно это делают через Mutex. Но умные люди на experts-exchange.com много лет назад посоветовали мне следующее:
      // в главном окне: protected procedure CreateParams(var Params: TCreateParams); override; procedure TMyApp.CreateParams(var Params: TCreateParams); begin inherited CreateParams(Params); Params.WinClassName:='MyAPP String ID'; end; // В файле .dpr: var AWnd, A1Wnd: HWND; AWnd:=FindWindow('MyApp String ID',nil); if IsWindow(AWnd) then begin A1Wnd := GetWindow(AWnd, GW_OWNER); if IsWindow(A1Wnd) then AWnd := A1Wnd; ShowWindow(AWnd, SW_RESTORE); SetForegroundWindow(AWnd); end else begin Application.Initialize; ....... end Способ хорош тем, что он не просто блокирует повторный запуск, а выводит уже запущенное приложение на передний план, тычет юзера в него носом. Но при переходе на FireMonkey возникла закавыка: компилятору не нравится TCreateParams. Говорит: undeclared identifier. Чем бы его заменить?
    • От Вадим Смоленский
      Перейдя с Берлина на Токио, столкнулся с неприятным феноменом. Раньше, выводя на экран форму с обилием TLabel, которые нужно было заполнить текстом и выровнять, я спокойно ставил везде AutoSize=True, присваивал нужные значения полям Text, после чего ширина каждого TLabel принимала новое значение, и мой код их красиво выравнивал. Теперь же ширина не меняется до полной отрисовки на экране всего и вся. Соответственно, на этапе выравнивания код оперирует со значениями ширины, которые были еще на этапе дизайна. Как следствие - всё наперекосяк! Что делать?
    • От Вадим Смоленский
      Windows, Berlin. Когда TWebBrowser на форме получает фокус, он начинает перехватывать все нажатия клавиш. До FormKeyDown управление уже не доходит. Отключение свойства браузера CanFocus не помогает, своего события OnKeyDown у него нет. Как быть? В идеале хотелось бы оставить браузеру навигационные клавиши (стрелки, PgDn, PgUp, Home, End), но все остальные отправлять на форму. Возможно ли это?
    • От Вадим Смоленский
      Работаю в Windows с FireDAC (SQLite). При каждом обращении к базе данных экранный курсор превращается в песочные часы с надписью SQL. Пользователи моего продукта далеки от программирования и ни про какой SQL не слыхивали, им это совершенно ни к чему. Как мне подавить этот феномен? В свойствах TFDConnection я ничего на эту тему не нахожу...
  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу