Alex
Alex

Reputation: 5658

SystemParametersInfoForDPI corrupts memory

I am trying to use new SystemParametersInfoForDPI function for High DPI application. However, my application crashes (fatal fault) immediately when I return to the caller.

Exception is an access violation when trying execute 00000000.

The parameter that I am trying to obtain is SPI_GETNONCLIENTMETRICS.

procedure TForm1.Button1Click(Sender: TObject);
var
  SystemParametersInfoForDpi: function(uiAction, uiParam: UINT; pvParam: Pointer; fWinIni, DPI: UINT): BOOL; stdcall;
  Metrics: TNonClientMetrics;
begin
  SystemParametersInfoForDpi := GetProcAddress(GetModuleHandle(user32), 'SystemParametersInfoForDpi');
  Win32Check(Assigned(SystemParametersInfoForDpi));

  FillChar(Metrics, SizeOf(Metrics), 0);
  Metrics.cbSize := SizeOf(Metrics);
  if SystemParametersInfoForDPI(SPI_GETNONCLIENTMETRICS, Metrics.cbSize, @Metrics, 0, 120) then
    Caption := 'OK'
  else
    Caption := 'FAIL'; 
end; // - crashes here; D2007; Win2016

What is wrong?

Upvotes: 0

Views: 984

Answers (1)

Alex
Alex

Reputation: 5658

Crash part of question - it seems to be a bug in certain Windows builds.

The code in question is not correct.

SystemParametersInfoForDPI work fine only when latest version of structure is used: it should be UNICODE and be defined for latest OS version (e.g. such fields as iPaddedBorderWidth - must be present). In other words, it should have a maximum size among all possible definitions.

This is outlined in documentation, but in a bit weird way:

  • Docs for SystemParametersInfoForDpi say: "only Unicode (LOGFONTW) strings are supported in this function"
  • Docs for SystemParametersInfo say: "The pvParam parameter must point to a NONCLIENTMETRICS structure that contains the new parameters"

While this does seem like clean up from docs on older Windows versions (2000, etc.) - but it really tells you that you absolutely must use latest versions of structures.

Finally, the bug is that SystemParametersInfoForDpi does not fail with "insufficient buffer" or "invalid parameter" error when it sees unsupported buffer size - as one might expect. Instead it will happily and silently perform buffer overflow. This happens only on some Windows builds.

Thus, if structure is allocated on the stack - buffer overflow will erase return address. And when your code will try to return to caller - it will crash.

Upvotes: 2

Related Questions