【问题标题】:WM_DPICHANGE message not received未收到 WM_DPICHANGE 消息
【发布时间】:2016-05-04 15:42:04
【问题描述】:

我正在使用 Delphi XE7 测试有关使应用程序感知 DPI 的问题。

其中一项任务是响应当窗口移动到具有不同 DPI 值的监视器时应生成的 WM_DPICHANGE 消息。我没有设置多显示器,所以我编写了一个非常简单的测试程序来生成此消息,但在 windows 8.1 平台上没有收到它。它在 Windows 7 上运行良好。以下程序演示了这一点:

unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

const
   WM_DPICHANGED = 736;  // 0x02E0

type
  TMyForm = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure DPIChanged(var Message: TMessage); message WM_DPICHANGED;
  public
    { Public declarations }
  end;

var
  MyForm: TMyForm;

implementation

{$R *.dfm}

procedure TMyForm.Button1Click(Sender: TObject);
begin
   PostMessage(Self.Handle,WM_DPICHANGED,0,0);
end;

procedure TMyForm.DPIChanged(var Message: TMessage);
begin
   ShowMessage('Message WM_DPICHANGED Received');
end;

end.

在 Windows 8.1 下运行时不显示 ShowMessage。有什么东西在消息到达我的程序之前就吃掉了它吗?

【问题讨论】:

  • 如果没有多台显示器,您就没有机会实现任何目标
  • 这将一事无成。即使您可以捕捉到该消息,如果没有第二台具有不同 DPI 设置的显示器,您也无法测试您如何响应它。这就像在没有 Android 设备或模拟器的情况下尝试编写 Android 应用程序一样;你所做的任何事情都是简单的猜测,注定会失败。
  • 另外,你读过the documentation,它说这条消息只会被注册为PROCESS_PER_MONITOR_DPI_AWARE的应用程序接收,对吧?

标签: windows delphi dpi


【解决方案1】:

WM_DPICHANGED 仅在 Windows 8.1 和更新版本上受支持。

当您说您的代码可以在 Windows 7 上运行时,实际上它的工作方式是单击按钮将消息发布到您的窗口,并且您正在捕获该消息。另一方面,它与实际 DPI 变化无关。在 Windows 7 上,WM_DPICHANGED 被视为您发送了一些自定义消息。

在 Windows 8.1 和更新版本上,WM_DPICHANGED 是 Windows API 定义的消息。使用 PostMessage 发送该消息失败,错误代码为 1159 ERROR_MESSAGE_SYNC_ONLY

该错误意味着您无法异步发送该特定消息。这里的问题在于这里的第二个参数,即在 Windows API 中定义的 WM_DPICHANGED 消息的情况下指向 RECT 结构的指针。

WM_DPICHANGED

  • wParam - wParam 的 HIWORD 包含窗口新 dpi 的 Y 轴值。 wParam 的 LOWORD 包含 窗口新 DPI 的 X 轴值。例如,96、120、144、 或 192. X 轴和 Y 轴的值相同 Windows 应用。
  • lParam - 一个指向 RECT 结构的 指针,该结构提供当前窗口的建议大小和位置,为新窗口缩放 DPI。期望应用程序将重新定位和调整窗口大小 根据处理此问题时 lParam 提供的建议 消息。

PostMessage

如果您将 WM_USER 以下范围内的消息发送到异步 消息函数(PostMessage、SendNotifyMessage 和 SendMessageCallback),其消息参数不能包含指针。 否则,操作将失败。函数将在之前返回 接收线程有机会处理消息并且 sender 将在内存使用前释放内存。

如果您将代码中的 PostMessage 更改为 SendMessage,您的按钮点击也将在 Windows 8.1+ 上运行。

当然,如果您正确设置了应用程序 DPI 感知,它通常也会处理 Windows 发送的WM_DPICHANGED 消息。

【讨论】:

  • 感谢您的解释。我担心我的窗口(通过清单可以识别 DPI)没有收到消息,因为这是 Delphi 正在做的事情。我现在只需要给自己一个多显示器测试平台。
  • “Windows 正在保护该队列不接收不是由系统直接发送的 WM_DPICHANGED 消息。” 对我来说看起来很可疑,sent 消息没有' t 甚至最终排在队列中。监视通常不会出现在队列中的消息会浪费资源。但是我无法测试/验证任何东西......
  • @SertacAkyuz 我为PostMessage 发表了评论,其中消息被发送到消息队列。很明显,它们并没有像预期的那样传递到窗口。我不知道到底发生了什么以及为什么会发生,您很可能必须是 MS 开发人员才能知道确切的细节。 SendMessage 的行为与我所写的不同。
  • @Dalija 你检查 PostMessage 的返回了吗?
  • @SertacAkyuz 不,我没有。谢谢你的提醒。这在图片中带来了全新的解释。当前的一个陷入清晨(或不那么早)的白痴。我会尽快更新答案。
猜你喜欢
  • 1970-01-01
  • 2018-05-15
  • 2014-02-17
  • 2012-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-17
  • 1970-01-01
相关资源
最近更新 更多