What does an application have to do in order "support" Remote Desktop Services?
Asked Answered
P

6

13

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 ?

Pendragon answered 31/1, 2011 at 18:24 Comment(0)
P
21

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.

Pignut answered 31/1, 2011 at 19:3 Comment(5)
The consumer of the Virtual Methods should check if the Session Message was for the consumer's Session. How will they do that without knowing the SessionId (you are not passing it)?Amerigo
@Amerigo I missed an important part of my code which I have now added as an edit to the answer. When I register for notification, I use NOTIFY_FOR_THIS_SESSION. I'd forgotten about that part of the code and of course it doesn't make sense to listen for a message that you aren't going to receive until you've requested notification! Thanks for spotting this.Pignut
@David: nice to see your fast edit! +1 btw: I think that permission issues (writing data into HKLM or program folder) and HKCU dependancies are by far the biggest issue.Amerigo
@Amerigo I think Eric mentioned those issues too. I personally have never been aware of those pitfalls because my app has always worked as standard user with restricted rights. It's funny how my answer gets the most up-votes (probably because it's got loads of code in), but the less flashy answers don't get so much love. Such is the way of SO!Pignut
@David: well you are being rewarded because you took the time for a detailed answer, nothing wrong with that!Amerigo
H
11

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).

Highly answered 31/1, 2011 at 18:27 Comment(2)
How long does it take you to whip up an entire custom component as an SO answer? I just upvoted all three of those - nice work!Abraham
@Abraham M: Generally less than an hour. I have done it so many times. Thanks!Highly
K
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.

Klagenfurt answered 31/1, 2011 at 20:46 Comment(2)
+1 good points. I'm not sure that most remote desktops operate with so few colours. For example the default settings no the Win7 machine I am at right now are for 32 bit colour. Anti-aliasing would best be handled by using the systems settings rather than hard coding. But that's best practice anyway and I believe is what happens by default with a Delphi app.Pignut
RDP on a Win7 machine is about the best situation you can face. IME when people go for RDP, they also often go for cheap clients, meaning bare-bones Linux-based all-in-one client boxes.Klagenfurt
S
4

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.

Suttle answered 31/1, 2011 at 21:32 Comment(0)
B
3

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.

Bogoch answered 1/2, 2011 at 0:17 Comment(0)
C
0

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.

Clothilde answered 1/2, 2011 at 1:46 Comment(2)
that code is not needed because the same checks exist throughout the VCL. Is it possible you were building up strings into the caption property over a number of lines of code.Pignut
Sorry. I was using controls other than TLabel that didn't have such checks. I believe the custom status bar control I was using was one such control. In retrospect, I probably should have fixed the custom control.Clothilde

© 2022 - 2024 — McMap. All rights reserved.