Update: Microsoft has provided a fix.
One of the security fixes included in the august 2017 updates for Windows contains a bug that causes graphics problems on multi monitor systems in some applications. This blog post contains the following information:
- Symptoms of the problem
- What triggers the problems
- Technical background
Symptoms of the problem
In applications, graphics or controls are not shown or shown distorted. You might for example only see an empty background with missing foreground graphics or missing controls. Or you see the Desktop or parts of a different application in parts of the currently active application.
These graphical problems only appear on secondary monitors, not on the main monitor and possibly also depending on the position of the application window within the secondary monitor.
What triggers the problems
The problem appears when all of the following conditions are met:
- Windows 7 (or Windows Server 2008)
- The patches of KB4034664 or KB4034679 are fully installed
- The currently logged on user is not a local System Administrator on the computer, but a user with limited rights or a domain user
- The system has multiple monitors
- The monitors are setup such that certain parts of the screen have negative screen positions. For example a secondary monitor is setup to be left of the main monitor. Or a monitor is setup to have a higher top border than the main monitor
- An application window is positioned so that it is within the area of the screen that has negative screen coordinates.
- This application window uses the Windows API function “StretchDIBits” (or a variant) to draw a graphic on a device context directly linked to that window.
These workarounds exist:
- Upgrade to Windows 10 / Server 2012
- Uninstall KB4034664 / KB4034679 patches from the system
- Log on with a user that is a full local administrator
- Only use the application on the main monitor, not a secondary monitor
- Arrange the monitors such that no part of a monitor has negative screen coordinates. (See details in next paragraph)
In Windows, when you have multiple monitors, one of these monitors becomes the main monitor. Screen coordinates are relative to the main monitor. So, screen coordinates can be negative when a monitor is positioned left of or higher than the main monitor. You can use the Windows control panel to change which monitor is the main monitor and to position the secondary monitors relative to the main monitor.
Consider the following setups. An area circled in red marks an area with negative screen coordinates where these graphics problems will occur.
The actual problem seems to be within the Windows API Function “StretchDIBits”. After the security update, when this function is used to draw graphics onto a part of the screen with negative screen coordinates, it fails and 1. does not actually draw the graphics on the screen and 2. actually returns the error code 0 to signal something went wrong. Microosft will have to fix this issue.
I could reproduce the issue in a very simple Delphi application. The application consists of a form with a TPaintBox. When the form is moved, it calls Invalidate on the form to ask Windows to repaint the form. In the OnPaint event of the paint box, a bitmap is painted on the paint box using StretchDIBits.
Here is the relevant source code:
procedure TForm1.PaintBox1Paint(Sender: TObject); var lResult: Integer; Bitmap: TBitmap; BitmapHeader: pBitmapInfo; BitmapImage : POINTER; HeaderSize : DWORD; ImageSize : DWORD; begin Bitmap:= TBitmap.Create; try Bitmap.LoadFromResourceName(HInstance, 'Bitmap_1'); GetDIBSizes(Bitmap.Handle, HeaderSize, ImageSize); GetMem(BitmapHeader, HeaderSize); GetMem(BitmapImage, ImageSize); try GetDIB(Bitmap.Handle, Bitmap.Palette, BitmapHeader^, BitmapImage^); lResult := StretchDIBits(Canvas.Handle, 0, 0, // Destination Origin 300, // Destination Width 300, // Destination Height 0, 0, // Source Origin 300, // Source Width & Height 300, BitmapImage, TBitmapInfo(BitmapHeader^), DIB_RGB_COLORS, SRCCOPY) ; Canvas.MoveTo(0,0); Canvas.LineTo(PaintBox1.Width, PaintBox1.Height); Label1.Caption := 'Result ' + IntToStr(lResult); if(lResult = 0) then begin Label1.Font.Color := clRed; end else begin Label1.Font.Color := clBlack; end; finally FreeMem(BitmapHeader); FreeMem(BitmapImage) end; finally FreeAndNil(Bitmap); end; end;
When running the application and moving the form, when the form is within an area of the screen with only positive screen coordinates, the bitmap is displayed and the label shows the expected value “300”. When the form is moved within an area with negative screen coordinates, the bitmap vanished and the label shows the function StretchDIBits returning the error code “0”.