我有一个需要通过远程桌面服务运行的 Delphi 程序。我应该注意什么会阻止它正常运行?
6 回答
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
使单个表单可以执行特定操作。SessionDisconnected
virtual
特别是,我所有的表格都调用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 参考了一篇题为“税收:远程桌面连接和绘画”的文章,这是我大部分代码的灵感来源。
我确信还有其他与远程桌面相关的问题,但双缓冲肯定是更重要的问题之一。
如果这是一个由成百上千用户运行的企业级或网络交付应用程序,请特别注意颜色深度、渐变和无聊的动画。尤其是最后一张。一个 16x16 旋转的“同步”图标不会占用太多带宽,但更大的东西可能会产生大量流量。当颜色级别下降时,渐变看起来很糟糕,而且压缩效果不好。当网络/系统管理员需要从网络中挤出更多内容时,颜色级别会下降。你可能无法控制它。这些担忧也适用于通常在没有完整桌面的情况下运行的 Citrix 应用程序。一个额外的考虑是,用户不会看到系统托盘通知区域。
Alpha-transparency 表单没有得到很好的 IME 支持(无论是客户端还是服务器,取决于它们的版本)。
您还必须担心颜色,大多数远程桌面以 16 位颜色运行,甚至只有 256 色,如果您有干净的现代 UI,您可能需要将其简化为“丑陋”模式,以确保一切正常可读。
同样,请注意抗锯齿文本,这在任何现代 UI 上都是必须的,但可能会导致在低色 RDP 上出现不可读的模糊字符。
通知气泡和其他基于 shell 的效果可能存在其他问题,因此您可能希望自己以常规形式处理它们,而不是依赖 Windows API 功能。
从图形上讲,双缓冲可能是一把双刃剑,在某些情况下(低调图形)关闭它可能是有益的,但如果您有更高级的渲染(渐变、透明位图、缩放位图、花式字体) ,抗锯齿线等)您可以通过双缓冲获得更好的外观和速度,因为轻微的延迟可能比带有闪烁的交互式绘图更好。
此外,一些位图可以比其他位图更快地通过,RDP 中使用的通常快速压缩有利于垂直渐变而不是水平渐变 fi
如果您的应用程序不必在普通用户帐户下运行,那么另一个问题将是文件权限。此外,特殊文件夹(如 temp)和注册表项可以具有不同的(动态)位置和限制。但是,如果您的应用程序已经在受限用户帐户下运行,那么您应该已经涵盖了所有内容。
对于 Citrix,我们遇到了打印机问题。有时连接将使用客户端在他们的机器上定义的打印机,有时打印机来自其他用户的会话,有时默认打印机位于我们用户无法访问的完全不同的位置。
我遇到了很多闪烁的问题,以至于它们无法通过远程桌面使用。这是因为我每秒更新很多次,例如字幕或状态面板。我更改了所有更新 UI 代码以检查标题是否实际更改:
if Str<>WeirdControl.Property1 then
WeirdControl.Property1 := Str; // minimize number of invalidates in case the control doesn't have it.
连同其他答案中提到的其他内容,这使我的应用程序在远程桌面情况下再次可用。