问题
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 ?
回答1:
In addition to the answers above, see also my answer to this question: Does anyone know about issues between Citrix and Delphi 2007 applications? (And perhaps other development languages?)
EDIT: On my blog I have a caterogy for Application Compatibility issues, they are real life stories of applications that behave badly on RDS/Citrix. Might be useful to check it out: http://www.remkoweijnen.nl/blog/topics/application-compatibility/
Other categories that may be of interest are http://www.remkoweijnen.nl/blog/topics/citrix/ and http://www.remkoweijnen.nl/blog/topics/terminalserver/ although not all may apply for your question.
2nd EDIT: You can use the Standard User Analyzer tool from Microsoft to check if your application is suitable to be run by an ordinary user (MS focuses on UAC but on a typical RDS or Citrix server there are only low privilegded users). http://technet.microsoft.com/en-us/library/cc765948(WS.10).aspx
回答2:
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.
回答3:
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).
回答4:
Alpha-transparency forms aren't well supported IME (either by the clients or the servers, depending on their versions).
You also have to worry about colors, most remote desktop operate in 16 bit colors, or even just 256 colors, if you have a clean modern UI, you may need to dumb it down into "ugly" mode so that everything is guaranteed to be readable.
In the same vein, beware of anti-aliased text, which is a must on any modern UI, but can result in unreadable blurred characters on low-color RDP.
There are can be other issues with notifications bubbles and other shell-based effects, so you'll probably want to handle them yourself, in regular forms, rather than rely on the Windows API functionality.
Graphically speaking, double-buffering can be a double-edged sword, in some cases (low-key graphics) it can be beneficial to turn it off, but if you have more advanced renderings (gradients, transparent bitmaps, scaled bitmaps, fancy fonts, anti-aliased lines etc.) you can get better looks and speed with double-buffering, as a slight lag can be better than interactive drawing with flickering.
Also some bitmaps can get through faster than others, the usual fast compressions used in RDP favour vertical gradients over horizontal gradients f.i.
Another issue will be with the files rights, if your application never had to run under a regular user account. Also special folders (like temp) and registry keys can have different (dynamic) locations, and restrictions. But if you're application as already been running under an restricted user account, you should already have everything covered.
回答5:
If this is an enterprise-wide or web-delivered app run by hundreds or thousands of users, pay particular attention to color depth, gradients, and frivilous animations. Especially that last one. A 16x16 spinning "sync" icon won't eat too much bandwidth, but larger things may generate a lot of traffic. Gradients look crappy when the color level goes down, and they don't compress well. Color levels go down when network/system admins need to squeeze more out of the network. You may not be able to control it. These concerns would also apply to Citrix apps, which typically run without a full desktop. One extra consideration with that, is that users won't see the system tray notification area.
回答6:
With Citrix, we have had issues with printers. Sometimes the connection will use the printers the client has defined on their machine, sometimes the printers are from other users' sessions, and other times the default printer is at a completely different location inaccessible to our user.
回答7:
I had many problems with things flickering so much that they were un-usable through remote desktop. This was because I was updating things like Captions or Status panels, many times per second. I changed all my update-UI code to check if a caption had actually changed:
if Str<>WeirdControl.Property1 then
WeirdControl.Property1 := Str; // minimize number of invalidates in case the control doesn't have it.
Along with other things mentioned in other answers, this made my applications usable again in remote desktop situations.
来源:https://stackoverflow.com/questions/4854534/what-does-an-application-have-to-do-in-order-support-remote-desktop-services