Что приложение должно сделать для того, чтобы "поддерживать" службы удаленных рабочих столов?
У меня есть Delphi-программа, которую нужно запускать через службы удаленных рабочих столов. Что я должен остерегаться, чтобы он не работал правильно?
7 ответов
В дополнение к ответам выше см. Также мой ответ на этот вопрос: кто-нибудь знает о проблемах между приложениями Citrix и Delphi 2007? (А может и другие языки разработки?)
РЕДАКТИРОВАТЬ: В моем блоге у меня есть caterogy для проблем совместимости приложений, это реальные истории приложений, которые плохо работают на RDS/Citrix. Может быть полезно проверить это: http://www.remkoweijnen.nl/blog/topics/application-compatibility/
Другие категории, которые могут представлять интерес, это http://www.remkoweijnen.nl/blog/topics/citrix/ и http://www.remkoweijnen.nl/blog/topics/terminalserver/ хотя не все могут подать заявку на ваш вопрос.
2-е РЕДАКТИРОВАНИЕ: Вы можете использовать инструмент Standard User Analyzer от Microsoft, чтобы проверить, подходит ли ваше приложение для запуска обычным пользователем (MS фокусируется на UAC, но на типичном сервере RDS или Citrix есть только пользователи с низкими привилегиями). http://technet.microsoft.com/en-us/library/cc765948(WS.10).aspx
Андреас прав в точной двойной буферизации. Это самый важный аспект, о котором я должен знать.
Как мягкий контрапункт, я не люблю двойную буферизацию вообще, потому что очень трудно сделать это правильно. Многие компоненты не имеют успеха. Я думаю о выпадающих списках 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
это перечислитель, который обходит дочерние элементы компонента.
Ссылка "Старая новая вещь" относится к статье под названием " Налоги: подключение к удаленному рабочему столу и рисование", которая послужила источником вдохновения для большей части этого кода.
Я уверен, что существуют другие проблемы, связанные с удаленным рабочим столом, но двойная буферизация, безусловно, является одной из наиболее важных.
Двойная буферизация - одна из таких вещей.
Вообще, двойная буферизация - отличная вещь, и я использую ее постоянно. Смотрите, например, мои компоненты (1), (2) и (3). Все они имеют двойную буферизацию и поэтому полностью свободны от мерцания (и просты в реализации), но если вы запускаете это удаленно, вам нужно отправлять битовые карты, а не команды GDI, поэтому это может быть довольно медленным (и неэкономичным).
Если это корпоративное или веб-приложение, которое запускают сотни или тысячи пользователей, обратите особое внимание на глубину цвета, градиенты и легкомысленную анимацию. Особенно последний. Вращающийся значок синхронизации 16x16 не потребляет слишком много трафика, но большие объекты могут генерировать большой трафик. Градиенты выглядят дрянно, когда уровень цвета снижается, и они плохо сжимаются. Цветовые уровни понижаются, когда сетевым / системным администраторам нужно выжать больше из сети. Возможно, вы не сможете это контролировать. Эти проблемы также относятся к приложениям Citrix, которые обычно работают без полноценного рабочего стола. Еще одно соображение заключается в том, что пользователи не будут видеть область уведомлений в системном трее.
Формы альфа-прозрачности плохо поддерживаются IME (ни клиентами, ни серверами, в зависимости от их версий).
Вам также нужно беспокоиться о цветах, большинство удаленных рабочих столов работают в 16-битных цветах, или даже 256 цветов, если у вас чистый современный пользовательский интерфейс, вам, возможно, придется перевести его в "уродливый" режим, чтобы все гарантированно было удобочитаемый.
В том же духе, остерегайтесь сглаженного текста, который необходим в любом современном пользовательском интерфейсе, но может привести к нечитаемым размытым символам на низкоцветном RDP.
Могут быть и другие проблемы с пузырьками уведомлений и другими эффектами на основе оболочки, поэтому вам, вероятно, захочется обрабатывать их самостоятельно, в обычных формах, а не полагаться на функциональность Windows API.
Графически говоря, двойная буферизация может быть обоюдоострым мечом, в некоторых случаях (неброская графика) может быть полезно отключить его, но если у вас есть более продвинутые рендеры (градиенты, прозрачные растровые изображения, масштабированные растровые изображения, необычные шрифты) сглаженные линии и т. д.) с двойной буферизацией можно улучшить внешний вид и скорость, поскольку небольшая задержка может быть лучше, чем при интерактивном рисовании с мерцанием.
Также некоторые растровые изображения могут проходить быстрее, чем другие, обычные быстрые сжатия, используемые в RDP, предпочитают вертикальные градиенты по горизонтальным градиентам.
Другая проблема будет связана с правами на файлы, если ваше приложение никогда не запускалось под учетной записью обычного пользователя. Также специальные папки (например, temp) и ключи реестра могут иметь разные (динамические) расположения и ограничения. Но если ваше приложение уже запущено под ограниченной учетной записью пользователя, у вас уже должно быть все покрыто.
С Citrix у нас были проблемы с принтерами. Иногда для подключения используются принтеры, которые клиент определил на своем компьютере, иногда принтеры из сеансов других пользователей, а в других случаях принтер по умолчанию находится в совершенно другом месте, недоступном для нашего пользователя.
У меня было много проблем с вещами, мерцающими настолько, что их нельзя было использовать через удаленный рабочий стол. Это было потому, что я обновлял такие вещи, как подписи или панели статуса, много раз в секунду. Я изменил весь свой код обновления пользовательского интерфейса, чтобы проверить, действительно ли изменилась подпись:
if Str<>WeirdControl.Property1 then
WeirdControl.Property1 := Str; // minimize number of invalidates in case the control doesn't have it.
Наряду с другими вещами, упомянутыми в других ответах, это сделало мои приложения снова применимыми в ситуациях удаленного рабочего стола.