FMX-Trayicon邮件处理 [英] FMX - Trayicon message handling

查看:105
本文介绍了FMX-Trayicon邮件处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法通过FMX(XE3,Windows)设置任务栏图标。我使用的代码可以在无数个线程中找到,但是我没有得到图标正常工作的消息处理。

I'm having trouble setting up a tray icon with FMX (XE3, Windows). I'm using the same code that can be found in countless threads but I did not get the message handling for the icon to work.

为了说明,我创建了一个在FormCreate中设置TrayIcon数据并使用按钮创建的testapp。

To ilustrate I've created a testapp that sets up the TrayIcon data in the FormCreate and creates it with a button. It will show the correct icon and the correct tooltip, the TrayMessage procedure will never get called though.

unit Unit2;

interface

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

const
  WM_ICONTRAY = WM_USER + 1;

type
  TForm2 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    TrayIconData: TNotifyIconData;
    procedure TrayMessage(var Msg: TMessage); message WM_ICONTRAY;
  end;

var
  Form2: TForm2;

implementation

{$R *.fmx}

procedure TForm2.Button1Click(Sender: TObject);
begin
  Shell_NotifyIcon(NIM_ADD, @TrayIconData);
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  with TrayIconData do
  begin
    cbSize := SizeOf;
    Wnd := FmxHandleToHWND(self.Handle);
    uID := 0;
    uFlags := NIF_MESSAGE + NIF_ICON + NIF_TIP;
    uCallbackMessage := WM_ICONTRAY;
    hIcon := GetClassLong(FmxHandleToHWND(self.Handle), GCL_HICONSM);
    StrPCopy(szTip, 'testapp');
  end;
end;

procedure TForm2.TrayMessage(var Msg: TMessage);
begin
  case Msg.lParam of
    WM_LBUTTONDOWN: ShowMessage('LBUTTON');
    WM_RBUTTONDOWN: ShowMessage('RBUTTON');
  end;
end;

end.

我已经使用VCL创建了相同的方案,并且按预期工作。唯一的区别是直接使用Form2.Handle而不是FMX转换(和Application.Handle来加载图标数据,但这不是FMX中的问题)。谁能指出正确的方向?

I have created the same scenario with VCL and it works as expected. The only difference is directly using Form2.Handle instead of the FMX conversion (and Application.Handle to load the icon data, but that's not part of the issue in FMX). Can anyone point me in the right direction ?

推荐答案

与VCL不同,FireMonkey不会将原始窗口消息分发给FMX控件进行自定义处理(这将违反跨平台框架的目的)。 FireMonkey在 FMX.Platform.Win 单元中实现了单个 WndProc()函数。 FireMonkey创建的c $ c> HWND 窗口。该实现处理需要处理的某些窗口消息,从而触发各种控制方法( WMPaint() KeyUp / Down() MouseUp / Down()等),然后将未处理的消息直接传递给 DefWindowProc()操作系统处理过程中,根本不让控件完全看到消息。

Unlike VCL, FireMonkey does not dispatch raw window messages to FMX controls for custom processing (that would defeat the purpose of a cross-platform framework). FireMonkey has a single WndProc() function implemented in the FMX.Platform.Win unit that is used for all HWND windows that FireMonkey creates. That implementation processes certain window messages that it needs to process, triggering various control methods accordingly (WMPaint(), KeyUp/Down(), MouseUp/Down(), etc), and then passes unprocessed messages directly to DefWindowProc() for OS processing, without letting controls see the messages at all.

因此,要访问原始消息的唯一方法是:

So, the only way you are going to gain access to the raw messages is to either:


  1. 创建自己的窗口,例如使用 AllocateHWnd() CreateWindow / Ex()直接。

连接到FireMonkey的 HWND 直接通过 Get / SetWindowLong / Ptr()打开Windows。由于FireMonkey是跨平台框架,并且 HWND 窗口是特定于平台的实现细节,所以我建议避免使用这种方法。

hook into FireMonkey's HWND windows directly via Get/SetWindowLong/Ptr(). Since FireMonkey is a cross-platform framework, and HWND windows are a platform-specific implementation detail, I would suggest avoiding this approach.

通过 SetWindowsHookEx()使用特定于线程的消息挂钩。通过使它们成为线程特定的,您不必编写DLL来实现该钩子。

use thread-specific message hooks via SetWindowsHookEx(). By making them thread-specific, you avoid having to write a DLL to implement the hook.

在这种特殊情况下,#1是您的最佳选择。任务栏图标是Windows特有的功能,因此您确实应该使用与FireMonkey无关的Windows特有代码来处理它们。您可以使用 AllocateHWnd()将您的Form类的方法(或任何类)用作 WndProc()用于接收托盘消息,同时仍允许Form类处理它们。例如:

In this particular situation, #1 is your best choice. Tray icons are a Windows-specific feature, so you really should use Windows-specific code that is not tied to FireMonkey to handle them. You can use AllocateHWnd() to use a method of your Form class (or any class, for that matter) as the WndProc() for receiving the tray messages while still allowing the Form class to process them. For example:

type
  TForm2 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    {$IFDEF MSWINDOWS}
    TrayWnd: HWND;
    TrayIconData: TNotifyIconData;
    TrayIconAdded: Boolean;
    procedure TrayWndProc(var Message: TMessage);
    {$ENDIF}
  public
    { Public declarations }
  end;

{$IFDEF MSWINDOWS}
const
  WM_ICONTRAY = WM_USER + 1;
{$ENDIF}

procedure TForm2.FormCreate(Sender: TObject);
begin
  {$IFDEF MSWINDOWS}
  TrayWnd := AllocateHWnd(TrayWndProc);
  with TrayIconData do
  begin
    cbSize := SizeOf(TrayIconData);
    Wnd := TrayWnd;
    uID := 1;
    uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
    uCallbackMessage := WM_ICONTRAY;
    hIcon := ...
    StrPCopy(szTip, 'testapp');
  end;
  {$ENDIF}
end;

procedure TForm2.FormDestroy(Sender: TObject);
begin
  {$IFDEF MSWINDOWS}
  if TrayIconAdded then
    Shell_NotifyIcon(NIM_DELETE, @TrayIconData);
  DeallocateHWnd(TrayWnd);
  {$ENDIF}
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
  {$IFDEF MSWINDOWS}
  if not TrayIconAdded then
    TrayIconAdded := Shell_NotifyIcon(NIM_ADD, @TrayIconData);
  {$ENDIF}
end;

{$IFDEF MSWINDOWS}
procedure TForm2.TrayWndProc(var Message: TMessage);
begin
  if Message.MSG = WM_ICONTRAY then
  begin
     ...
  else
    Message.Result := DefWindowProc(TrayWnd, Message.Msg, Message.WParam, Message.LParam);
end;
{$ENDIF}

这篇关于FMX-Trayicon邮件处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆