【问题标题】:Can't detect when Windows Font Size has changed C++ MFC无法检测 Windows 字体大小何时更改 C++ MFC
【发布时间】:2010-09-17 15:51:42
【问题描述】:

我正在尝试确定如何检测用户何时将 Windows 字体大小从普通字体更改为特大字体,通过在 Windows XP 机器上执行以下步骤来选择字体大小:

  1. 右键单击桌面并选择属性。
  2. 单击外观选项卡。
  3. 选择字体大小:普通/大字体/超大字体

我的理解是字体大小的变化会导致 DPI 的变化,所以这是我到目前为止所尝试的。


我的目标:

我想检测 Windows 字体大小 何时从普通字体变为大字体或超大字体,并根据字体大小的变化采取一些措施。我假设当 Windows 字体大小发生变化时,DPI 也会发生变化(尤其是当大小为超大字体时


到目前为止我尝试过的:

我收到了几条消息,包括:WM_SETTINGCHANGE、WM_NCCALCSIZE、WM_NCPAINT 等...但是这些消息都不是字体大小更改时所特有的,换句话说,当我收到 WM_SETTINGSCHANGE 消息时,我想知道什么改变了。

理论上,当我定义 OnSettingChange 并且 Windows 调用它时,lpszSection 应该告诉我正在更改的部分是什么,并且工作正常,但随后我通过调用 SystemParametersInfo 检查给定部分并传入操作 SPI_GETNONCLIENTMETRICS,并且我单步调试调试器,并确保查看返回的 NONCLIENTMETRICS 中的数据是否有任何字体更改,但没有发生任何字体更改。

即使这不起作用,我应该仍然能够在设置更改时检查 DPI。我真的不会关心其他细节,每次收到 WM_SETTINGCHANGE 消息时,我都会检查 DPI 并执行我感兴趣的操作,但我也无法获取系统 DPI。

我尝试通过调用 GetSystemMetrics 方法来获取 DPI,同样适用于每个 DC:

Dekstop DC->GetDeviceCaps LOGPIXELSX/LOGPIXELSY 窗口 DC->GetDeviceCaps LOGPIXELSX/LOGPIXELSY 当前 DC->GetDeviceCaps LOGPIXELSX/LOGPIXELSY

即使我在图形属性窗口中更改 DPI,这些值也不会返回任何不同的值,它们总是显示 96。

有人可以帮我解决这个问题吗?我应该寻找什么?我应该在哪里看?

afx_msg void CMainFrame::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
    int windowDPI = 0;
    int deviceDPI = 0;
    int systemDPI = 0;
    int desktopDPI = 0;
    int dpi_00_X = 0;
    int dpi_01_X = 0;
    int dpi_02_X = 0;
    int dpi_03_X = 0;

    CDC* windowDC = CWnd::GetWindowDC(); // try with window DC
    HDC desktop = ::GetDC(NULL); // try with desktop DC
    CDC* device = CWnd::GetDC(); // try with current DC
    HDC hDC = *device; // try with HDC
    if( windowDC )
    {
        windowDPI = windowDC->GetDeviceCaps(LOGPIXELSY); 
        // always 96 regardless if I change the Font 
        // Size to Extra Large Fonts or keep it at Normal

        dpi_00_X = windowDC->GetDeviceCaps(LOGPIXELSX); // 96
    }

    if( desktop )
    {
        desktopDPI = ::GetDeviceCaps(desktop, LOGPIXELSY); // 96
        dpi_01_X = ::GetDeviceCaps(desktop, LOGPIXELSX); // 96
    }

    if( device )
    {
        deviceDPI = device->GetDeviceCaps(LOGPIXELSY); // 96
        dpi_02_X = device->GetDeviceCaps(LOGPIXELSX); // 96
    }

    systemDPI = ::GetDeviceCaps(hDC, LOGPIXELSY); // 96
    dpi_03_X = ::GetDeviceCaps(hDC, LOGPIXELSX); // 96

    CWnd::ReleaseDC(device);
    CWnd::ReleaseDC(windowDC);
    ::ReleaseDC(NULL, desktop);
    ::ReleaseDC(NULL, hDC);

    CWnd::OnWinSettingChange(uFlags, lpszSection);
}

DPI 始终返回 96,但是当我将字体大小更改为超大字体或将 DPI 更改为 120(从图形属性中)时,设置更改才会生效。

【问题讨论】:

    标签: c++ winapi mfc font-size system-font


    【解决方案1】:

    [重读后编辑] 我几乎肯定更改为“大字体”不会导致 DPI 更改,而是主题设置。您应该能够通过应用“大字体”更改然后打开 DPI 设置所在的高级显示属性来验证,它应该保持在 96dpi。


    DPI 更改应该需要重新启动。也许该设置尚未传播到 GetDeviceCaps 可以检索它的地方?

    也许尝试更改一个不需要重新启动的设置(也许是分辨率),然后看看您是否可以检测到更改。如果可以,您的答案可能是直到重新启动后才能检测到 DPI 变化。

    【讨论】:

    • 但是更改字体大小不需要重新启动,并且更改无需重新启动即可生效...系统必须向 Windows 发送某种消息,否则他们怎么知道更改字体大小?
    • 对多次重新编辑感到抱歉,我只需要确保从代码中删除任何不相关的信息...我尝试了您的一些解决方案,我不得不回来重新编辑所以我在这里有最新的 cmets :) 再次抱歉!感谢您的帮助。
    • DPI 设置更改需要注销和登录(从 Windows 7 开始)
    【解决方案2】:

    当您在桌面 DC 上调用 GetDeviceCaps() 时,您是否使用了可能被 MFC 缓存并因此包含过期信息的 DC?您是否从 OnSettingsChange 处理程序内部同步调用 GetDeviceCaps()?我可以看到这些东西中的一个或两个可能如何让您获得过时的 DPI 版本。

    Raymond Chen wrote about this 和他的解决方案看起来像这样(请注意,我添加了 :: 运算符以避免调用 API 的 MFC 包装器):

    int GetScreenDPI()
    {
      HDC hdcScreen = ::GetDC(NULL);
      int iDPI = -1; // assume failure
      if (hdcScreen) {
        iDPI = ::GetDeviceCaps(hdcScreen, LOGPIXELSX);
        ::ReleaseDC(NULL, hdcScreen);
      }
      return iDPI;
    }
    

    【讨论】:

    【解决方案3】:

    我有预感 WM_THEMECHANGED 会照顾好你。不过,它没有任何关于发生了什么变化的暗示。您必须使用 OpenThemeData 并缓存初始设置,然后在每次收到消息时进行比较。

    您可能不需要关心发生了什么变化,难道您不能有一个通用的布局例程,通过考虑所有因素并假设从头开始来调整您的表单/对话框/其他内容吗?

    你想解决什么问题?

    【讨论】:

    • 长话短说,基本上最初的开发人员没有意识到 Windows 字体大小会发生变化,并且当它发生变化时他们也没有优雅地处理这种情况:)......现在我必须这样做:)。我会尝试您的建议,如果可行,我会尽快为您更新:)。
    • 字体大小更改只是您需要处理的情况之一。你真的应该看看这样的布局引擎:codeproject.com/KB/MFC/UltimateToolbox_Layout.aspx。 codeproject 上有很多不错的。
    【解决方案4】:

    参见http://msdn.microsoft.com/en-us/library/ms701681(VS.85).aspx,那里对此进行了解释(引用:“如果不取消 dpi 缩放,此调用将返回默认值 96 dpi。”)

    【讨论】:

      【解决方案5】:

      我认为字体大小改变时显示 DPI 不会改变。 Windows 可能只是将WM_PAINTWM_NCPAINT 消息发送到所有打开的窗口,并且它们正在使用当前(现在很大)系统字体重新绘制自己。

      【讨论】:

        【解决方案6】:

        查看注册表中的这些值:

        Windows XP 主题 HKCU\Software\Microsoft\Windows\CurrentVersion\ThemeManager\SizeName 可能的值:NormalSize、LargeFonts 和 ExtraLargeFonts 这些值与语言无关

        Windows 经典主题 HKCU\控制面板\外观\当前 可能的值:Windows 经典、Windows 经典(大)、Windows 经典(特大)、Windows 标准、Windows 标准(大)、Windows 标准(特大) 请注意,这些值取决于语言

        Windows Vista 不支持此功能。如果我们想要更大的字体,只需更改 DPI 设置。在这种情况下,GetDeviceCaps 应该可以工作。

        希望这会有所帮助。

        【讨论】:

          猜你喜欢
          • 2011-11-28
          • 2018-08-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-12-31
          • 2012-03-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多