• 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

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

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 для винды. Иначе ни один контрол работать и рисоваться не будет.

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


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

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

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

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

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

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

Войти

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

Войти

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

    • От Вадим Смоленский
      В декабре я задавал здесь вопрос о борьбе с перехватами нажатий клавиш компонентом TWebBrowser. Продвинутый пользователь Kami посоветовал тогда, раз уж меня интересует только Windows, поставить хук на клавиатуру. Поделился полезной ссылкой. Добавил, что можно еще много чего нагуглить. Что-то действительно нагуглилось - но не в том объеме, чтобы я смог четко понять, как это следует делать. Вопросов много. Куда именно должна быть воткнута функция KeyboardProc? Что в ней должно содержаться, чтобы управление передавалось уже написанному обработчику события FormKeyDown? Многие также упоминают о возникающих проблемах с юникодом, и хорошо было бы понять, как уберечься от них.
      Буду очень признателен, если кто-нибудь осветит эту темную для меня материю.
    • От Вадим Смоленский
      Как преобразовать тип HString в обычную строку?
      Нагуглил упоминания о функции TWindowsString.HStringToString, которая должна находиться в System.WinrtHelpers. Но такого юнита в поставке Delphi не наблюдаю. Может, его можно где-нибудь раздобыть? Или существуют иные способы?
    • От Вадим Смоленский
      Пытаясь разобраться с проблемой размещения файлов в UWP-приложениях, пришел к необходимости создать в своем коде объект класса ApplicationData. Этот класс описан на соответствующей странице майкрософтовской документации, где в самом начале обозначено следующее:
      Namespace:  Windows.Storage
      Assemblies:  Windows.Storage.dll, Windows.dll
      Юнита с именем Windows.Storage или Winapi.Windows.Storage я в поставке Delphi не наблюдаю. Неудивительно, что попытки вставить соответствующие идентификаторы в раздел uses ни к чему не ведут. Как в таких случаях поступают? Откуда берут необходимое?
    • От Вадим Смоленский
      Хочу еще раз обратиться к коллективному разуму в надежде все-таки разобраться с тем, как должен быть устроен пакет appx для размещения в Microsoft Store. А именно - как организовать размещение настроек и пользовательских файлов в специально отведенных для этого папках, а не в установочном каталоге, что запрещено.
      Вся информация, которую я смог к сегодняшнему дню накопать, размещена на этой странице майкрософтовской документации и сводится к тому, что при инсталляции пакета appx автоматически создается хранилище пользовательских данных из трех папок. Цитирую: one for local files, one for roaming files, and one for temporary files. В общем-то, это удобно - по крайней мере, не нужно заботиться о деинсталляции: ровно эти же папки при удалении программы и сотрутся.
      Вопрос в том, как к этому хранилищу обратиться. В идеале, конечно же, хотелось бы иметь возможность уже в Deployment Manager обозначить для некоторых файлов, что они должны быть положены в это хранилище, а не в установочный каталог. Но как это можно сделать и можно ли сделать вообще, мне понять не удалось. Списка констант для параметра Remote Path я найти не смог, а отдельные упоминаемые тут и там константы ('res', 'assets', 'classes', 'library') явно относятся к мобильным платформам, а не к Windows.
      Если так поступить нельзя, остается класть всё в установочный каталог, а при первом запуске приложения переносить в нужное место. Но опять же: как приложению заполучить полный адрес этого места? Ведь это вовсе не привычные нам CSIDL_APPDATA и не CSIDL_COMMON_APPDATA, это нечто новое, доселе невиданное, в документации называемое "local app data store". На той же странице есть пример соответствующего кода с использованием класса ApplicationData, но он написан на незнакомом мне C#. Там есть также ссылки на описание класса ApplicationData, но по ссылкам тоже C# и C++. Был бы очень признателен, если бы кто-нибудь показал мне, как заполучить адрес local app data store средствами Delphi.
    • От Вадим Смоленский
      Целый месяц бодаюсь с Microsoft Store, пытаясь разместить у них свое UWP-приложение в виде пакета appx. Получаю отлуп за отлупом. Проблема: приложение должно располагать папкой для пользователя, отличной от установочного каталога, где он мог бы сохранять файлы. Там же должны храниться файлы настроек. Раньше, создавая дистрибутив в InnoSetup, я всегда предусматривал создание такой папки по адресу <user>\AppData\Roaming\MyApp. Теперь всё полагается указывать в разделе Deployment - но как там указать такой адрес? Вроде бы, есть графа "Remote path", и логично существовать каким-то макросам для нее, вроде AppData - но никакой информации на эту тему я найти не смог. Видел лишь упоминания таких макросов, как res, assets, classes, library - однако все они, как я понял, относятся к мобильным платформам.
      Попытки обходных маневров не удались. Сначала я решил всё класть в установочный каталог, а при первом запуске создавать нужную папку и перебрасывать в нее несколько файлов. На моем компьютере это работает, но при тестировании в Microsoft Store отчего-то валится (присылают скриншот с сообщением "Access is denied"). То ли нельзя стирать файлы в установочном каталоге, то ли нельзя в таком режиме создавать новый каталог. Потом я прочитал где-то, что UWP-приложениям всё равно, где лежат файлы, они могут их менять даже в установочном каталоге. Попробовал всё валить в одну кучу и так работать. Увы, опять отрапортовали о падении ("The app tries to create a file under WindowsApps folder").
      Получается, что все-таки нужно как-то заставить appx-дистрибутив создавать при установке папку по адресу <user>\AppData\Roaming\MyApp. Но как?
      Спрашивал на experts-exchange, там знатоков не нашлось. Если и здесь нет, может, кто-нибудь хотя бы подскажет, в каких местах есть смысл спросить?
    • От Гумар Садиков
      Добрый день форумчане ! Подскажите пожалуйста, как можно использовать при объявлении процедур и функций горячие клавиши ? ( Редакция : Delphi XE7 )Как то в разделе private: 
             procedure AddListItem(list: array of string; itype: string); 
      Выделить это все, и какие клавиши нажать ? Чтоб процедура оказалась в теле:
      procedure TForm1.AddListItem(list: array of string; itype: string);
      var
      LItem: TListBoxItem;
      begin
       
      end;
    • От Вадим Смоленский
      Проект для Windows, автономная БД SQLite, никаких клиент-серверных дел, компоненты и операции самые простые: SQL-запрос в TFDQuery, вызов метода Open. Всё работает нормально, но отдельные пользователи жалуются на эпизодические непредсказуемые падения при поиске. Интересно, что после каждого такого падения всё опять функционирует нормально, но потом приложение не удается нормально закрыть, приходится вызывать диспетчер задач.
      Стабильно воспроизвести не могут ни пользователи, ни я сам. Мне удалось это считанные несколько раз - я лишь смог убедиться через отладчик, что проблема при закрытии связана именно с базой данных: вставлял в обработчик FormCloseQuery оператор TFDConnection.Close - и программа пару раз упала именно на этом операторе.
      Найти корень зла пока не удалось. Показалось только, что проблема возникает лишь тогда, когда поиск приводит к слишком большому (несколько тысяч) числу записей в TFDQuery.
      Может, стоит поменять какие-нибудь установки в TFDConnection или TFDQuery ?
    • От Вадим Смоленский
      Упаковываю свое приложение в appx при помощи кнопки Deploy в Deployment Manager. При этом в разделе Опций "Manifest File" выставлено "Auto Generate". В итоге получается файл AppxManifest.xml; насколько я понимаю, этот манифест также включается в состав итогового пакета appx.
      Пробую загрузить получившийся appx в Microsoft Store. Грузится очень долго и в итоге выдает ошибку: "You don't have permissions to specify the following namespaces in the appx manifest file of the package MyApp.appx: restricted namespace: http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
      В файле AppxManifest.xml действительно отыскивается такой фрагмент. Убираю его, заодно убираю фрагмент  <Capabilities> ... </Capabilities> (если не убрать, получившийся xml даже не отобразится в браузере), переименовываю в MyApp.manifest, меняю установку для манифеста на "Custom", указываю имя. Всё повторяю. Результат ровно тот же.
      Полностью меняю содержимое MyApp.manifest, беря шаблон отсюда. Всё повторяю. Результат опять тот же. Опять магазину не нравится фрагмент манифеста насчет restricted capabilities, которого теперь, по идее, там быть не должно.
      Такое ощущение, что при формировании пакета appx не имеют никакого значения установки по поводу манифеста. Он всегда запихивается в appx в некоем дефолтном варианте, повлиять на который невозможно.
      Или все-таки возможно?
    • От Вадим Смоленский
      При упаковке Windows-приложения в appx все дополнительные файлы приходится класть в одну папку с исполнимым файлом, ибо Deployment Manager, судя по всему, не предусматривает возможности сразу положить их в AppData. Но я все-таки хочу, чтобы некоторые файлы были легко доступны пользователям, поэтому организовал приложение так, что оно при первом запуске создает новый каталог в AppData:
      MyDirectory:=GetSpecialFolderPath(CSIDL_APPDATA)+'\MyApp'; CreateDir(MyDirectory); TDirectory.SetAttributes(MyDirectory,[TFileAttribute.faNormal]); После этого в созданный каталог переносятся некоторые файлы, и всё работает хорошо, за исключением одного момента: этот новый каталог невозможно открыть, например, в Проводнике. Из самого приложения легко можно открыть диалоговое окно и увидеть в нем этот каталог и все файлы; можно их читать и в них писать, но вне приложения этот каталог невидим. Всё равно что не существует.
      Мне казалось, что присвоение каталогу атрибута faNormal дожно все проблемы решить. Увы, не решает. В чем тут закавыка?
    • От Вадим Смоленский
      Устанавливая и запуская свое Windows-приложение, предназначенное для Microsoft Store и запакованное в appx, наткнулся на интересный феномен. Чтобы узнать адрес текущего каталога, я всегда использовал функцию SysUtils.GetCurrentDir. Полученный полный адрес был мне нужен, например, чтобы показывать в TWebBrowser файлы хелпов (относительные адреса там почему-то не прокатывают). Раньше адресом текущего каталога всегда был адрес, где лежит исполнимый файл - условно говоря, C:\Program Files\MyApp. Теперь, когда пакет создается по принципам UWP,  исполнимый файл и прочее хозяйство кладется в каталог C:\Program Files\WindowsApps\MyApp_1.0.0.0_x86__sp51hrchc9zqj.  При этом функция GetCurrentDir почему-то возвращает совершенно другой адрес, а именно C:\WINDOWS\system32. Соответственно, TWebBrowser ничего не показывает.
      Функция SysUtils.GetDir ведет себя так же.
      Как быть?
  • Последние посетители   0 пользователей онлайн

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