Что нужно делать приложением для поддержки служб удаленного рабочего стола?

У меня есть программа Delphi, которую нужно запускать с помощью служб удаленных рабочих столов. Что я должен делать, чтобы остановить его работу?

Ответы

Ответ 1

В дополнение к ответам выше, см. также мой ответ на этот вопрос: Кто-нибудь знает о проблемах между приложениями Citrix и Delphi 2007? (И, возможно, другие языки разработки?)

EDIT: В моем блоге у меня есть справочная информация о проблемах совместимости приложений, это реальные истории приложений, которые плохо работают на RDS/Citrix. Может быть полезно проверить это: http://www.remkoweijnen.nl/blog/topics/application-compatibility/

Другие категории, которые могут представлять интерес, http://www.remkoweijnen.nl/blog/topics/citrix/ и http://www.remkoweijnen.nl/blog/topics/terminalserver/, хотя не все могут подать заявку на ваш вопрос.

2nd EDIT:. Вы можете использовать средство стандартного анализатора пользователей от Microsoft, чтобы проверить, подходит ли ваше приложение для обычного пользователя (MS фокусируется на UAC, но на типичном сервере RDS или Citrix есть только низкоприоритетные пользователи). http://technet.microsoft.com/en-us/library/cc765948(WS.10).aspx

Ответ 2

Андреас прав в двойной буферизации. Это самый важный аспект, который я считаю осведомленным.

Как мягкая контрастная точка, мне не нравится двойная буферизация в целом, потому что очень сложно ее правильно исправить. Многим компонентам не удается. Я думаю о выпадающих списках 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 Old Thing на статью, озаглавленную Налоги: подключение к удаленному рабочему столу и роспись, что было моим вдохновением для большей части этого кода.

Я уверен, что есть и другие проблемы, связанные с удаленным рабочим столом, но двойная буферизация, безусловно, является одной из наиболее важных.

Ответ 3

Двойная буферизация - одна из таких вещей.

Обычно двойная буферизация - это замечательная вещь, и я использую ее все время. См. Например, мои компоненты (1), (2) и (3). Все они имеют двойную буферизацию и, следовательно, полностью свободны от мерцания (и их легко реализовать), но если вы запускаете это дистанционно, вам нужно отправлять растровые изображения, а не команды GDI, поэтому он может быть довольно медленным (и неэкономичным).

Ответ 4

Альфа-прозрачные формы плохо поддерживаются IME (либо клиентами, либо серверами в зависимости от их версий).

Вам также нужно беспокоиться о цветах, большинство удаленных рабочих столов работают с 16-битными цветами или даже с 256 цветами, если у вас есть чистый современный пользовательский интерфейс, вам может понадобиться отключить его в "уродливый" режим, чтобы все было гарантированно быть читаемым.
В том же духе, остерегайтесь сглаженного текста, который является обязательным для любого современного пользовательского интерфейса, но может привести к нечитаемым размытым символам на низкоуровневом RDP.

Существуют и другие проблемы с пузырьками уведомлений и другими эффектами на основе оболочки, поэтому вы, вероятно, захотите обрабатывать их самостоятельно, в обычных формах, а не полагаться на функциональность Windows API.

Графически говоря, двойная буферизация может быть обоюдоострым мечом, в некоторых случаях (сдержанной графикой) может быть полезно отключить его, но если у вас есть более продвинутые визуализации (градиенты, прозрачные растровые изображения, масштабированные растровые изображения, причудливые шрифты, сглаженные линии и т.д.), вы можете улучшить внешний вид и скорость с двойной буферизацией, так как небольшое отставание может быть лучше, чем интерактивный рисунок с мерцанием.
Также некоторые растровые изображения могут проходить быстрее, чем другие, обычные быстрые сжатия, используемые в RDP, поддерживают вертикальные градиенты по горизонтальным градиентам f.i.

Другая проблема будет связана с правами на файлы, если ваше приложение никогда не запускалось под обычной учетной записью пользователя. Кроме того, специальные папки (например, temp) и ключи реестра могут иметь разные (динамические) местоположения и ограничения. Но если вы используете приложение, которое уже работает под учетной записью с ограниченным доступом, вы должны уже все покрыть.

Ответ 5

Если это приложение для всего предприятия или веб-приложений, которое запускается сотнями или тысячами пользователей, обратите особое внимание на глубину цвета, градиенты и фривизовую анимацию. Особенно последний. 16x16 вращающийся "синхронизирующий" значок не будет потреблять слишком много полосы пропускания, но большие вещи могут генерировать много трафика. Градиенты выглядят дерьмовыми, когда уровень цвета снижается, и они плохо сжимаются. Уровни цвета снижаются, когда администраторы сети/системы должны выжимать больше из сети. Возможно, вы не сможете его контролировать. Эти проблемы будут также применяться к приложениям Citrix, которые обычно запускаются без полного рабочего стола. Еще одно соображение заключается в том, что пользователи не будут видеть область уведомлений в системном трее.

Ответ 6

С Citrix у нас были проблемы с принтерами. Иногда соединение будет использовать принтеры, которые клиент определил на своем компьютере, иногда принтеры из сеансов других пользователей, а в других случаях принтер по умолчанию находится в совершенно другом месте, недоступном для нашего пользователя.

Ответ 7

У меня было много проблем с такими мерцающими вещами, что они были недоступны через удаленный рабочий стол. Это произошло потому, что я обновлял такие вещи, как Captions или Status, много раз в секунду. Я изменил весь свой код обновления-UI, чтобы проверить, действительно ли надпись была изменена:

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

Наряду с другими вещами, упомянутыми в других ответах, это снова применило мои приложения в ситуациях удаленного рабочего стола.