在delphi7中使用TScreen

在delphi7中使用TScreen,delphi,delphi-7,Delphi,Delphi 7,我的Delphi-7应用程序显示: Screen.DesktopWidth Screen.DesktopHeight Screen.Monitors[0].Width Screen.Monitors[0].Height 如果选择了第二个监视器,还应: Screen.Monitors[1].Width Screen.Monitors[1].Height 在WinXP Pro PC上运行应用程序后,我转到控制面板/显示器/设置,并更改第二个显示器的设置(添加或删除) 然后

我的Delphi-7应用程序显示:

Screen.DesktopWidth  
Screen.DesktopHeight  
Screen.Monitors[0].Width  
Screen.Monitors[0].Height  
如果选择了第二个监视器,还应:

Screen.Monitors[1].Width  
Screen.Monitors[1].Height  
在WinXP Pro PC上运行应用程序后,我转到控制面板/显示器/设置,并更改第二个显示器的设置(添加或删除)

然后单击刷新按钮显示4(或6)个参数的新值,意外情况发生了:Screen.DesktopWidth和Screen.DesktopHeight显示正确的新值,但其他2(或4)个参数的值非常错误

与Screen.Monitors[0]类似。宽度=5586935,而它应该是1680


在Delphi 7中使用TScreen是否有一些特殊规则?

多亏了TLama,我在Delphi 7中找到了解决TScreen问题的方法

“导致”问题的原始代码:

LabMon1.Caption := ' Mon 1: ' + IntToStr (Screen.Monitors[0].Width) +
                   ' x ' + IntToStr (Screen.Monitors[0].Height);

if (Screen.MonitorCount = 1)
then LabMon2.Caption := ' Mon 2: -'
else LabMon2.Caption := ' Mon 2: ' + IntToStr (Screen.Monitors[1].Width) +
                        ' x ' + IntToStr (Screen.Monitors[1].Height);
我只需添加一行代码即可解决此问题:

LabMon1.Caption := ' Mon 1: ' + IntToStr (Monitor.Width) +
                   ' x ' + IntToStr (Monitor.Height) ;

LabMon1.Caption := ' Mon 1: ' + IntToStr (Screen.Monitors[0].Width) +
                   ' x ' + IntToStr (Screen.Monitors[0].Height);

if (Screen.MonitorCount = 1)
then LabMon2.Caption := ' Mon 2: -'
else LabMon2.Caption := ' Mon 2: ' + IntToStr (Screen.Monitors[1].Width) +
                        ' x ' + IntToStr (Screen.Monitors[1].Height);

再次感谢TLama,感谢您对本问题主题做出的巨大贡献

Screen.Monitors数组包含无效值,如果在程序运行时切换用户。我们使用这行代码强制屏幕对象更新列表:

Screen.MonitorFromWindow(0, mdNull);

由于连接或断开显示器或USB显示设备时TScreen的刷新问题(错误)而出现此问题。@Dave82的答案不适合我。函数MonitorFromWindow的结果必须返回另一个值(未知/无效值),以强制更新TScreen对象

下面的这个骗局就是这样的:

确保multimon位于uses子句中:

uses
 multimon;
将其添加到界面(表单的)部分

将此添加到实施部分(表单)


希望有人在找这个的时候能帮上忙。当您想要检测显示设备设置更改(分辨率和方向)时,请捕获WM_DISPLAYCHANGE事件。

我无法模拟它,因为我有一台显示器和Delphi 2009,但我想问题可能在于显示器列表刷新(在Delphi 2009中,这是通过私有过程
Screen.GetMonitors
完成的)。我想当您重新启动应用程序时,您得到了正确的值,不是吗?如果我没记错,也许Sertac在某个地方写道,销毁
屏幕
实例并重新创建它是安全的。如果是这样,那么下面应该刷新这些数据
Screen.Free;Screen:=TScreen.create(nil);
,但我真的不知道这个操作有多安全。您是在引用TMonitor实例屏幕。监视器[0]还是每次都在获取屏幕。监视器[0]?@TLama“我想您在重新启动应用程序时得到了正确的值,不是吗?”没错。当在显示4(或6)个参数的语句之前有一个ShowMessage语句时,我也会得到正确的值。ShowMessage将导致消息队列被抽取。但我不希望队列消息在这里起作用。因此,我认为您的主监视器已更改其句柄和函数(内部调用
GetMonitorInfo
)由于没有实际的句柄(并返回随机值)而失败。问题似乎在于监视器列表(
TScreen.FMonitors
)被缓存且不会更改(在任何时候?我必须查看一下…)。同时尝试检查
监视器.Width
(之前没有
屏幕
,只有
监视器.Width
)。这应该顺便说一句。更新
屏幕。监视器
缓存列表,以防通过
MonitorFromWindow
调用获得的监视器不包含在该列表中。不客气!实际上,您不需要使用
监视器.Width
监视器.Height
或将其值分配到某个位置。触摸属性就足够了rty的getter会更新
屏幕。当在列表中找不到主监视器的句柄时,会监视该列表。我只是将其用于宽度检查。如果您运气好,并且编译器没有消除这样的语句,则只使用
监视器;
什么可以强制getter执行我所描述的操作。但是但不知何故,这仍然是一种肮脏的方式。这个bug在最近版本的Delphi中仍然存在吗?(比如XE7)谢谢Codebeat!我在20多年的时间里搜索了这个bug的解决方案:-)我仍然在旧项目中使用Delphi 5。但是我不得不将EnumMonitorsProc函数移到源代码的顶部。这对我来说很有效。让我们看看它是否也适用于USB监视器。。。。
protected
procedure WMDeviceChange(var Msg: TMessage); message WM_DEVICECHANGE;
    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;
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;