Skip to content

Commit

Permalink
Bug, SystemParametersInfoForDpi解决Dpi传递0时结果异常,此外已经限制Action只接受SPI_GETIC…
Browse files Browse the repository at this point in the history
…ONTITLELOGFONT、SPI_GETICONMETRICS、SPI_GETNONCLIENTMETRICS
  • Loading branch information
mingkuang-Chuyu committed Jun 30, 2024
1 parent a2d7a26 commit 29a93a8
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 82 deletions.
170 changes: 88 additions & 82 deletions src/Thunks/user32.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,104 +412,110 @@ namespace YY::Thunks
BOOL,
WINAPI,
SystemParametersInfoForDpi,
_In_ UINT uiAction,
_In_ UINT uiParam,
_Pre_maybenull_ _Post_valid_ PVOID pvParam,
_In_ UINT fWinIni,
_In_ UINT dpi
_In_ UINT _uAction,
_In_ UINT _uParam,
_Pre_maybenull_ _Post_valid_ PVOID _pParam,
_In_ UINT _fWinIni,
_In_ UINT _uDpi
)
{
if (auto const pSystemParametersInfoForDpi = try_get_SystemParametersInfoForDpi())
{
return pSystemParametersInfoForDpi(uiAction, uiParam, pvParam, fWinIni, dpi);
}
if (auto const _pfnSystemParametersInfoForDpi = try_get_SystemParametersInfoForDpi())
return _pfnSystemParametersInfoForDpi(_uAction, _uParam, _pParam, _fWinIni, _uDpi);

if (!SystemParametersInfoW(uiAction, uiParam, pvParam, fWinIni))
return FALSE;
// SystemParametersInfoW 函数始终拿到的是 DPI = _uBaseDpi 的情况
// 其结果与当前系统DPI值无关。
// 我们可以通过缩放来模拟 SystemParametersInfoForDpi
const auto _uBaseDpi = internal::GetDpiForSystemDownlevel();

if (SPI_GETICONTITLELOGFONT == uiAction
|| SPI_GETICONMETRICS == uiAction
|| SPI_GETNONCLIENTMETRICS == uiAction)
switch (_uAction)
{
auto nDpiX = internal::GetDpiForSystemDownlevel();

if (nDpiX != dpi)
{
if (SPI_GETICONTITLELOGFONT == uiAction)
{
if (auto pInfo = (LOGFONTW*)pvParam)
{
pInfo->lfHeight = MulDiv(pInfo->lfHeight, dpi, nDpiX);
}
}
else if (SPI_GETICONMETRICS == uiAction)
{
if (auto pInfo = (ICONMETRICSW*)pvParam)
{
if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, iHorzSpacing))
pInfo->iHorzSpacing = MulDiv(pInfo->iHorzSpacing, dpi, nDpiX);

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, iVertSpacing))
pInfo->iVertSpacing = MulDiv(pInfo->iVertSpacing, dpi, nDpiX);

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, lfFont))
pInfo->lfFont.lfHeight = MulDiv(pInfo->lfFont.lfHeight, dpi, nDpiX);
}
}
else if (SPI_GETNONCLIENTMETRICS == uiAction)
{
if (auto pInfo = (NONCLIENTMETRICSW*)pvParam)
{
if(RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, iBorderWidth))
pInfo->iBorderWidth = MulDiv(pInfo->iBorderWidth, dpi, nDpiX);

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, iScrollWidth))
pInfo->iScrollWidth = MulDiv(pInfo->iScrollWidth, dpi, nDpiX);

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, iScrollHeight))
pInfo->iScrollHeight = MulDiv(pInfo->iScrollHeight, dpi, nDpiX);

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, iCaptionWidth))
pInfo->iCaptionWidth= MulDiv(pInfo->iCaptionWidth, dpi, nDpiX);

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, iCaptionHeight))
pInfo->iCaptionHeight= MulDiv(pInfo->iCaptionHeight, dpi, nDpiX);

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, lfCaptionFont))
pInfo->lfCaptionFont.lfHeight = MulDiv(pInfo->lfCaptionFont.lfHeight, dpi, nDpiX);

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, iSmCaptionWidth))
pInfo->iSmCaptionWidth = MulDiv(pInfo->iSmCaptionWidth, dpi, nDpiX);

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, iSmCaptionHeight))
pInfo->iSmCaptionHeight = MulDiv(pInfo->iSmCaptionHeight, dpi, nDpiX);
case SPI_GETICONTITLELOGFONT:
{
if (!SystemParametersInfoW(_uAction, _uParam, _pParam, _fWinIni))
return FALSE;

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, lfSmCaptionFont))
pInfo->lfSmCaptionFont.lfHeight = MulDiv(pInfo->lfSmCaptionFont.lfHeight, dpi, nDpiX);
if(_pParam == nullptr || _uDpi == 0 || _uDpi == _uBaseDpi)
return TRUE;

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, iMenuWidth))
pInfo->iMenuWidth = MulDiv(pInfo->iMenuWidth, dpi, nDpiX);
auto _pInfo = (LOGFONTW*)_pParam;
_pInfo->lfHeight = MulDiv(_pInfo->lfHeight, _uDpi, _uBaseDpi);
return TRUE;
}
case SPI_GETICONMETRICS:
{
if (!SystemParametersInfoW(_uAction, _uParam, _pParam, _fWinIni))
return FALSE;

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, iMenuHeight))
pInfo->iMenuHeight = MulDiv(pInfo->iMenuHeight, dpi, nDpiX);
if (_pParam == nullptr || _uDpi == 0 || _uDpi == _uBaseDpi)
return TRUE;

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, lfMenuFont))
pInfo->lfMenuFont.lfHeight = MulDiv(pInfo->lfMenuFont.lfHeight, dpi, nDpiX);
auto _pInfo = (ICONMETRICSW*)_pParam;
_pInfo->iHorzSpacing = MulDiv(_pInfo->iHorzSpacing, _uDpi, _uBaseDpi);
_pInfo->iVertSpacing = MulDiv(_pInfo->iVertSpacing, _uDpi, _uBaseDpi);
_pInfo->lfFont.lfHeight = MulDiv(_pInfo->lfFont.lfHeight, _uDpi, _uBaseDpi);
return TRUE;
}
case SPI_GETNONCLIENTMETRICS:
{
// 微软强制要求Size等于 sizeof(NONCLIENTMETRICSW)
auto _pInfo = (NONCLIENTMETRICSW*)_pParam;
if (_pInfo == nullptr || _pInfo->cbSize != sizeof(NONCLIENTMETRICSW))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, lfStatusFont))
pInfo->lfStatusFont.lfHeight = MulDiv(pInfo->lfStatusFont.lfHeight, dpi, nDpiX);
if (!SystemParametersInfoW(_uAction, _uParam, _pParam, _fWinIni))
return FALSE;

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, lfMessageFont))
pInfo->lfMessageFont.lfHeight = MulDiv(pInfo->lfMessageFont.lfHeight, dpi, nDpiX);
if (_pParam == nullptr || _uDpi == 0)
return TRUE;

if (RTL_CONTAINS_FIELD(pInfo, pInfo->cbSize, iPaddedBorderWidth))
pInfo->iPaddedBorderWidth = MulDiv(pInfo->iPaddedBorderWidth, dpi, nDpiX);
}
// GetSystemMetrics(SM_CYBORDER) 的大小始终跟 Dpi无关
const auto _nSystemBorderWidth = MulDiv(GetSystemMetrics(SM_CYBORDER), _uDpi, USER_DEFAULT_SCREEN_DPI);
if (_uDpi == _uBaseDpi)
{
// 如果DPI恰好 == _uBaseDpi,这时可以优化,避免无意义的MulDiv调用。
// 注意,主动传入DPI时,iBorderWidth默认为 GetSystemMetrics(SM_CYBORDER)。
const auto _nBorderWidth = _pInfo->iBorderWidth + _pInfo->iPaddedBorderWidth;
if (_nBorderWidth > 0)
{
_pInfo->iPaddedBorderWidth = _nBorderWidth - _nSystemBorderWidth;
_pInfo->iBorderWidth = _nSystemBorderWidth;
}
return TRUE;
}

// 跳过 iBorderWidth,通过后续的iPaddedBorderWidth调整。
// _pInfo->iBorderWidth = MulDiv(_pInfo->iBorderWidth, _uDpi, kBaseDpi);
_pInfo->iScrollWidth = MulDiv(_pInfo->iScrollWidth, _uDpi, _uBaseDpi);
_pInfo->iScrollHeight = MulDiv(_pInfo->iScrollHeight, _uDpi, _uBaseDpi);
_pInfo->iCaptionWidth = MulDiv(_pInfo->iCaptionWidth, _uDpi, _uBaseDpi);
_pInfo->iCaptionHeight = MulDiv(_pInfo->iCaptionHeight, _uDpi, _uBaseDpi);
_pInfo->lfCaptionFont.lfHeight = MulDiv(_pInfo->lfCaptionFont.lfHeight, _uDpi, _uBaseDpi);
_pInfo->iSmCaptionWidth = MulDiv(_pInfo->iSmCaptionWidth, _uDpi, _uBaseDpi);
_pInfo->iSmCaptionHeight = MulDiv(_pInfo->iSmCaptionHeight, _uDpi, _uBaseDpi);
_pInfo->lfSmCaptionFont.lfHeight = MulDiv(_pInfo->lfSmCaptionFont.lfHeight, _uDpi, _uBaseDpi);
_pInfo->iMenuWidth = MulDiv(_pInfo->iMenuWidth, _uDpi, _uBaseDpi);
_pInfo->iMenuHeight = MulDiv(_pInfo->iMenuHeight, _uDpi, _uBaseDpi);
_pInfo->lfMenuFont.lfHeight = MulDiv(_pInfo->lfMenuFont.lfHeight, _uDpi, _uBaseDpi);
_pInfo->lfStatusFont.lfHeight = MulDiv(_pInfo->lfStatusFont.lfHeight, _uDpi, _uBaseDpi);
_pInfo->lfMessageFont.lfHeight = MulDiv(_pInfo->lfMessageFont.lfHeight, _uDpi, _uBaseDpi);

const auto _nBorderWidth = _pInfo->iBorderWidth + _pInfo->iPaddedBorderWidth;
if (_nBorderWidth > 0)
{
_pInfo->iPaddedBorderWidth = MulDiv(_nBorderWidth, _uDpi, _uBaseDpi) - _nSystemBorderWidth;
_pInfo->iBorderWidth = _nSystemBorderWidth;
}
return TRUE;
}
}

return TRUE;
// 微软原版这时不会返回错误代码,但是总感觉是微软的Bug
// 我们这里就随便返回一个错误代码吧。
SetLastError(ERROR_NOT_SUPPORTED);
return FALSE;
}
#endif

Expand Down
64 changes: 64 additions & 0 deletions src/YY-Thunks.UnitTest/User32.UnitTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,68 @@ namespace User32
Assert::IsFalse(::AdjustWindowRectExForDpi(&_Current, 0, 0, 0, 0));
}
};

TEST_CLASS(SystemParametersInfoForDpi)
{
AwaysNullGuard Guard;

public:
SystemParametersInfoForDpi()
{
Guard |= YY::Thunks::aways_null_try_get_SystemParametersInfoForDpi;
}

TEST_METHOD(常规测试)
{
struct TestItem
{
UINT uAction;
UINT uParam;
UINT uDpi;
};

static constexpr TestItem kTestItems[] =
{
{ SPI_GETICONTITLELOGFONT, sizeof(LOGFONTW), 0 },
{ SPI_GETICONTITLELOGFONT, sizeof(LOGFONTW), USER_DEFAULT_SCREEN_DPI },
{ SPI_GETICONTITLELOGFONT, sizeof(LOGFONTW), USER_DEFAULT_SCREEN_DPI * 1.5 },
{ SPI_GETICONTITLELOGFONT, sizeof(LOGFONTW), USER_DEFAULT_SCREEN_DPI * 2 },

{ SPI_GETICONMETRICS, sizeof(ICONMETRICSW), 0 },
{ SPI_GETICONMETRICS, sizeof(ICONMETRICSW), USER_DEFAULT_SCREEN_DPI },
{ SPI_GETICONMETRICS, sizeof(ICONMETRICSW), USER_DEFAULT_SCREEN_DPI * 1.5 },
{ SPI_GETICONMETRICS, sizeof(ICONMETRICSW), USER_DEFAULT_SCREEN_DPI * 2 },

{ SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), 0 },
{ SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), USER_DEFAULT_SCREEN_DPI },
{ SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), USER_DEFAULT_SCREEN_DPI * 1.5 },
{ SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), USER_DEFAULT_SCREEN_DPI * 2 },
};

for (auto& _Item : kTestItems)
{
union
{
LOGFONTW LogFont;
ICONMETRICSW IconMeterics;
NONCLIENTMETRICSW NonClientMeterocs;
} _Target, _Current;

if (SPI_GETICONTITLELOGFONT != _Item.uAction)
{
_Target.IconMeterics.cbSize = _Item.uParam;
_Current.IconMeterics.cbSize = _Item.uParam;
}
::SystemParametersInfoForDpi(_Item.uAction, _Item.uParam, &_Current, 0, _Item.uDpi);

YY::Thunks::aways_null_try_get_SystemParametersInfoForDpi = false;
::SystemParametersInfoForDpi(_Item.uAction, _Item.uParam, &_Target, 0, _Item.uDpi);
YY::Thunks::aways_null_try_get_SystemParametersInfoForDpi = true;

CStringW _szMessage;
_szMessage.Format(L"Action = %d", _Item.uAction);
Assert::IsTrue(memcmp(&_Current, &_Target, _Item.uParam) == 0, _szMessage.GetString());
}
}
};
}

0 comments on commit 29a93a8

Please sign in to comment.