I have a Delphi program that needs to be run via Remote Desktop Services. What should I look out for that would stop it running properly ?
To work with Remote Desktop Services, the PCs must be running a Windows operating system, have the RDP display protocol installed, and have a live network connection using TCP/IP and a valid IP address.
Remote Desktop Services (RDS) is an umbrella term for features of Microsoft Windows Server that allow users to remotely access graphical desktops and Windows applications.
Andreas is correct in pin-pointing double-buffering. This is the most important aspect to consider that I am aware of.
As a mild counter point, I don't like double-buffering in general because it's very hard to get it right. Many components don't succeed. I'm thinking of VCL drop down list boxes which don't draw right under Windows Basic. There are others!
But some controls really do need double-buffering to avoid flicker, so what do you do? You want the benefit of double buffering when the user is locally connected, but you don't want to tax them with the network bandwidth when they are remote.
So, here's what I do:
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;
All forms in my app descend from TBaseForm
and so inherit this behaviour. The SessionConnected
and SessionDisconnected
methods are virtual
so individual forms can take specific actions.
In particular, all my forms call 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
is an enumerator that walks the children of a component.
The Old New Thing reference is to an article entitled Taxes: Remote Desktop Connection and painting which was my inspiration for much of this code.
I'm sure that there are other issues relating to remote desktop, but double-buffering is certainly one of the more important ones.
Double buffering is one such thing.
Generally double-buffering is a great thing, and I use it all the time. See for instance my components (1), (2), and (3). They are all double-buffered, and therefore completely free from flickering (and easy to implement), but if you run this remotely, you have to send bitmaps and not GDI commands, so it might be rather slow (and uneconomical).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With