13

我有一个需要通过远程桌面服务运行的 Delphi 程序。我应该注意什么会阻止它正常运行?

4

6 回答 6

21

Andreas 在精确定位双缓冲方面是正确的。这是我所知道的最重要的考虑因素。

作为一个温和的反驳点,我一般不喜欢双缓冲,因为很难做到正确。许多组件没有成功。我正在考虑 VCL 下拉列表框,它不能在 Windows Basic 下正确绘制。还有其他的!

但是有些控件确实需要双缓冲来避免闪烁,那你怎么办呢?当用户在本地连接时,您希望获得双缓冲的好处,但您不想在他们远程时对他们的网络带宽征税。

所以,这就是我所做的:

procedure WMWTSSessionChange(var Message: TMessage); message WM_WTSSESSION_CHANGE;

procedure TBaseForm.WMWTSSessionChange(var Message: TMessage);
begin
  case Message.WParam of
  WTS_CONSOLE_DISCONNECT,WTS_REMOTE_DISCONNECT,
  WTS_SESSION_LOCK,WTS_SESSION_LOGOFF:
    SessionDisconnected;
  WTS_CONSOLE_CONNECT,WTS_REMOTE_CONNECT,
  WTS_SESSION_UNLOCK,WTS_SESSION_LOGON:
    SessionConnected;
  end;
  inherited;
end;

function WTSRegisterSessionNotification(hWnd: HWND; dwFlags: DWORD): BOOL; stdcall; external 'Wtsapi32.dll';
function WTSUnRegisterSessionNotification(hWnd: HWND): BOOL; stdcall; external 'Wtsapi32.dll';

const
  NOTIFY_FOR_THIS_SESSION = 0;
  NOTIFY_FOR_ALL_SESSIONS = 1;

procedure TBaseForm.CreateWnd;
begin
  inherited;
  WTSRegisterSessionNotification(WindowHandle, NOTIFY_FOR_THIS_SESSION);
end;

procedure TBaseForm.DestroyWnd;
begin
  WTSUnRegisterSessionNotification(WindowHandle);
  inherited;
end;

我的应用程序中的所有表单TBaseForm都继承并继承了这种行为。和方法SessionConnected使单个表单可以执行特定操作。SessionDisconnectedvirtual

特别是,我所有的表格都调用UpdateDoubleBuffered

function InRemoteSession: Boolean;
begin
  Result := Windows.GetSystemMetrics(SM_REMOTESESSION)<>0;
end;

class procedure TBaseForm.UpdateDoubleBuffered(Control: TWinControl);
var
  DoubleBuffered: Boolean;
begin
  if InRemoteSession then begin
    //see The Old New Thing, Taxes: Remote Desktop Connection and painting
    DoubleBuffered := False;
  end else begin
    DoubleBuffered := (Control is TCustomListView) 
                   or (Control is TCustomStatusBar);
    //TCustomListView flickers when updating without double buffering
    //TCustomStatusBar has drawing infidelities without double buffering in my app
  end;
  Control.DoubleBuffered := DoubleBuffered;
end;

procedure TBaseForm.UpdateDoubleBuffered;
var
  Control: TControl;
begin
  for Control in ControlEnumerator(TWinControl) do begin
    UpdateDoubleBuffered(TWinControl(Control));
  end;
end;

ControlEnumerator是遍历组件子级的枚举器。

Old New Thing 参考了一篇题为“税收:远程桌面连接和绘画”的文章,这是我大部分代码的灵感来源。

我确信还有其他与远程桌面相关的问题,但双缓冲肯定是更重要的问题之一。

于 2011-01-31T19:03:13.357 回答
10

双缓冲就是这样一种东西。

通常双缓冲是一件很棒的事情,我一直都在使用它。例如,参见我的组件(1)(2)(3)。它们都是双缓冲的,因此完全没有闪烁(并且易于实现),但是如果您远程运行它,您必须发送位图而不是 GDI 命令,因此它可能会相当慢(并且不经济)。

于 2011-01-31T18:27:04.077 回答
4

如果这是一个由成百上千用户运行的企业级或网络交付应用程序,请特别注意颜色深度、渐变和无聊的动画。尤其是最后一张。一个 16x16 旋转的“同步”图标不会占用太多带宽,但更大的东西可能会产生大量流量。当颜色级别下降时,渐变看起来很糟糕,而且压缩效果不好。当网络/系统管理员需要从网络中挤出更多内容时,颜色级别会下降。你可能无法控制它。这些担忧也适用于通常在没有完整桌面的情况下运行的 Citrix 应用程序。一个额外的考虑是,用户不会看到系统托盘通知区域。

于 2011-01-31T21:32:53.357 回答
4

Alpha-transparency 表单没有得到很好的 IME 支持(无论是客户端还是服务器,取决于它们的版本)。

您还必须担心颜色,大多数远程桌面以 16 位颜色运行,甚至只有 256 色,如果您有干净的现代 UI,您可能需要将其简化为“丑陋”模式,以确保一切正常可读。
同样,请注意抗锯齿文本,这在任何现代 UI 上都是必须的,但可能会导致在低色 RDP 上出现不可读的模糊字符。

通知气泡和其他基于 shell 的效果可能存在其他问题,因此您可能希望自己以常规形式处理它们,而不是依赖 Windows API 功能。

从图形上讲,双缓冲可能是一把双刃剑,在某些情况下(低调图形)关闭它可能是有益的,但如果您有更高级的渲染(渐变、透明位图、缩放位图、花式字体) ,抗锯齿线等)您可以通过双缓冲获得更好的外观和速度,因为轻微的延迟可能比带有闪烁的交互式绘图更好。
此外,一些位图可以比其他位图更快地通过,RDP 中使用的通常快速压缩有利于垂直渐变而不是水平渐变 fi

如果您的应用程序不必在普通用户帐户下运行,那么另一个问题将是文件权限。此外,特殊文件夹(如 temp)和注册表项可以具有不同的(动态)位置和限制。但是,如果您的应用程序已经在受限用户帐户下运行,那么您应该已经涵盖了所有内容。

于 2011-01-31T20:46:10.163 回答
3

对于 Citrix,我们遇到了打印机问题。有时连接将使用客户端在他们的机器上定义的打印机,有时打印机来自其他用户的会话,有时默认打印机位于我们用户无法访问的完全不同的位置。

于 2011-02-01T00:17:58.767 回答
0

我遇到了很多闪烁的问题,以至于它们无法通过远程桌面使用。这是因为我每秒更新很多次,例如字幕或状态面板。我更改了所有更新 UI 代码以检查标题是否实际更改:

  if Str<>WeirdControl.Property1 then
      WeirdControl.Property1 := Str; // minimize number of invalidates in case the control doesn't have it.

连同其他答案中提到的其他内容,这使我的应用程序在远程桌面情况下再次可用。

于 2011-02-01T01:46:50.427 回答