Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using TScreen in Delphi 7

My Delphi-7 application displays :

Screen.DesktopWidth  
Screen.DesktopHeight  
Screen.Monitors[0].Width  
Screen.Monitors[0].Height  

and , if there's a second monitor selected , also :

Screen.Monitors[1].Width  
Screen.Monitors[1].Height  

With the application running on my WinXP-Pro PC , I go to Control Panel / Display / Settings , and change the settings for the second monitor (either add or remove it) .

I then click on a Refresh button to display the new values of the 4 (or 6) parameters , and something unexpected happens : Screen.DesktopWidth and Screen.DesktopHeight show the correct new values , but the values of the other 2 (or 4) parameters are very wrong .

Like Screen.Monitors[0].Width = 5586935 , while it should be 1680 .

Are there some special rules for using TScreen in Delphi 7 ?

like image 642
Ruud Schmeitz Avatar asked Jun 23 '12 07:06

Ruud Schmeitz


2 Answers

Came here because of refresh problem (bug) of TScreen when connect or disconnect a monitor or USB display device. The answer of @Dave82 doesn't work for me. The result of the function MonitorFromWindow must return another value (unknown/invalid value) to force an update of the TScreen object.

This cheat below does the trick:

Be sure multimon is in the uses clause:

uses
 multimon;

Add this to the interface part (of the form)

protected
procedure WMDeviceChange(var Msg: TMessage); message WM_DEVICECHANGE;

Add this to implementation part (of the form)

    function cheatMonitorFromWindow(hWnd: HWND; dwFlags: DWORD): HMONITOR; stdcall;
    begin
      // Does nothing, returns zero to force invalidate
     Result:=0;
    end;

    procedure TForm1.WMDeviceChange(var Msg: TMessage);
    var
     iCurrDisplayCount    : LongInt;
     iNewDisplayCount     : LongInt;
     pMonitorFromWinProc  : TMonitorFromWindow;

    begin
     iCurrDisplayCount:=Screen.MonitorCount;
     // Force monitor update, fix bug in customform, won't update at display change.
     // This a hack/cheat to multimon MonitorFromWindow func, it's fakes the result.
     // This is required to tell customform.getMonitor() to update the TScreen object.
     pMonitorFromWinProc:=MonitorFromWindow;      // Backup pointer to dynamic assigned DLL func  
     MonitorFromWindow:=cheatMonitorFromWindow;   // Assign cheat func 
     monitor;                                     // call the monitor property that calls customform.getMonitor and cheatfunc
     MonitorFromWindow:=pMonitorFromWinProc;      // restore the original func
     // ==========
     iNewDisplayCount:=Screen.MonitorCount;
     if( iCurrDisplayCount <> iNewDisplayCount ) then
     begin
       // Display count change!
     end;  
end;

What happen inside customform (code in Forms.pas)?

function TCustomForm.GetMonitor: TMonitor;
var
  HM: HMonitor;
  I: Integer;
begin
  Result := nil;
  HM := MonitorFromWindow(Handle, MONITOR_DEFAULTTONEAREST);
  for I := 0 to Screen.MonitorCount - 1 do
    if Screen.Monitors[I].Handle = HM then
    begin
      Result := Screen.Monitors[I];
      Exit;
    end;

  //if we get here, the Monitors array has changed, so we need to clear and reinitialize it
  for i := 0 to Screen.MonitorCount-1 do
    TMonitor(Screen.FMonitors[i]).Free;
  Screen.FMonitors.Clear;
  EnumDisplayMonitors(0, nil, @EnumMonitorsProc, LongInt(Screen.FMonitors));
  for I := 0 to Screen.MonitorCount - 1 do
    if Screen.Monitors[I].Handle = HM then
    begin
      Result := Screen.Monitors[I];
      Exit;
    end;    
end;

Hopes it helps when somebody is looking for this. When you want to detect display device settings changes (resolution and orientation), catch the WM_DISPLAYCHANGE event instead.

like image 188
Codebeat Avatar answered Oct 31 '22 13:10

Codebeat


Screen.Monitors array contain invalid values if you switch user while your program is running. We use this line of code to force the Screen object to update lists:

Screen.MonitorFromWindow(0, mdNull);
like image 1
Dave82 Avatar answered Oct 31 '22 13:10

Dave82