Игроки Зазеркалья. Виртуальный рабочий стол
http://raxp.radioliga.com
(специально для Интернетомании)
Сегодня мы рассмотрим пример практической реализации простейшего виртуального рабочего стола…

Рис. 1. Виртуальный WindowsXP в среде MojoPac
Сегодня мало-кого можно удивить виртуальными машинами. Эти “создания” уже прочно вошли в жизнь, как системных администраторов, так и рядовых пользователей. Несомненно, ради чего ставят виртуальную машину (VM) – это возможность ”потестить” неизвестную систему типа BSD, QNX или LINUX, отладить проект под ось, которая физически отсутствует и т.д., но мало кто задумывается о дополнительных (основных) функциях, таких как их сетевые интерфейсы. К примеру, вы можете установить прокси-сервер в виртуальной машине и ходить через него в Интернет, вы можете запустить неизвестное приложение, если не доверяете антивирусу и посмотреть что-будет с системой… или вы сами пишете вирус. Кроме-того, не всегда обязательно пользоваться самими виртуальными машинами, достаточно бывает и их меньших собратьев – “виртуальных” рабочих столов.
Краткий экскурс…
Что же такое – виртуальные машины? Как правило - это программы эмулирующие функциональность настоящего компьютера, с возможностью установить операционную систему с сопутствующими приложениями и позволяющие изменять свою аппаратную конфигурацию. Например, настройки сетевых интерфейсов или объем памяти. Причем, сама эмулирующая программа – называется виртуальной машиной, а система из под которой она запущена – host (носителем) системой.
В то же время наблюдается тенденция развития виртуальной среды в сторону переносимости (портируемости). Это значит, что, установив такую виртуальную машину, скажем на USB накопитель, вы получаете полноценную операционку, которую можно носить с собой и запускать прямо с флешки, т.е. вы можете не расставаться с излюбленными приложениями в любой среде.
Обзор существующих “виртуальных” столов. Рекомендации к использованию
В случае-же, когда использование виртуальных машин избыточно и требуется лишь больше пространства для работы, а места на мониторе или экране КПК не хватает, в игру вступают – виртуальные рабочие столы (десктопы). Для управления несколькими рабочими столами существует ряд утилит, схожих между собой как по возможностям, так и по функциональности. Как правило, они не имеют визуальных окон и при запуске сразу прячутся в трей. Рассмотрим вкратце некоторые из наиболее популярных:
Aston 1.91 http://www.astonshell.ru/files/aston.zip
Достоинства: индивидуальные настройки каждого стола, цветовые схемы, анимация, русский интерфейс
Недостатки: высокие требования к ресурсам, требует инсталляции, “глюки” при настройке
Статус: платный
Общая оценка: 2
Eiruk http://eiruk.at.tut.by/files/eirukliteru_1_7.zip
Достоинства: возможность создания до 10 десктопов
Недостатки: требует инсталляции
Статус: бесплатный
Общая оценка: 3
ManageDesk 2.30 http://www.managebytes.com/files/managedesk.exe
Достоинства: дополнительные функции по управлению системой, как-то CD-ROM, выключение компьютера
Недостатки: требует инсталляции
Статус: платный
Общая оценка: 3
WindowTool1.3 http://www.nuonsoft.com/cgi-bin/dl/dl.pl?id=ExaWare_WindowTool_1_3_Setup.exe
Достоинства: настраиваемые окна
Недостатки: требует инсталляции
Статус: бесплатный
Общая оценка: 4
Cool Desk 3.86 http://www.shelltoys.com/cdsetup.zip
Достоинства: индивидуальные настройки окон и клавиш горячего вызова, создание до 9 десктопов
Недостатки: требует инсталляции
Статус: бесплатный
Общая оценка: 4
BossKey 2.00 http://keir.net/download/bosskey.zip
Достоинства: не требует инсталляции, настраиваемые горячие клавиши, малый размер (96кБ)
Недостатки: нет индивидуальной настройки каждого стола
Статус: бесплатный
Общая оценка: 5
Все вышеозначенные приложения были протестированы по критерию работоспособности и дружелюбности к пользователю, согласно которым и были даны оценки. И крайне негативные отзывы сложились, к сожалению, по Aston-у. При установке, данная утилита насильно перезагружает систему не спрашивая пользователя, а ставя его перед фактом. Настройки режимов загрузки в ней не сохраняются. Ее нельзя удалить при ее сеансе… получилось лишь в безопасном режиме. Очень жаль, что при внешней красоте российская разработка так себя ведет, притом, что она еще и платная. Таким образом, общая оценка программы – заслуженно - 2. В то же время, не может не радовать миниатюрная бесплатная утилита BossKEY, с успехом выполнившая все измывательства и придирки, что выдвигает ее на первые места…
Предпосылки реализации ПО. Возможные решения
Вы уже задаетесь вопросом, какова-же основная идея создания нашего “виртуального” рабочего стола? Все достаточно просто. Как известно, все в windows- системе является процессами*.
* в то время как в linux, процессы представляют собой файлы на диске
Каждый из процессов может порождать окна (на то он и windows). Сами окна могут находиться в минимизированном, свернутом и невидимом состоянии. Вот, вот это-то свойство окон и положено в основу алгоритма программы. Представьте себе, что вы запомнили список всех видимых окон на данный текущий момент, а потом сделали их все – невидимыми (hidden). Что произойдет? По сути – ваш рабочий стол очистится и будет свободно пространство для открытия еще окон приложений. А что-же будет, если мы после этого откроем еще n- приложений, сделаем их невидимыми и опять сохраним новый список паралельно со старым? Правильно, мы получим опять ”чистый” рабочий стол. Где-же, опять спросите вы, “виртуальные” рабочие столы? Да вот-же, вспомните, что мы сохраняли каждый раз списки окон, перед тем как делать их невидимыми. Следовательно, если с помощью некоего интерфейса управления каждый раз восстанавливать (делать видимыми) наши сохраненные списки, то мы будем тем самым переключаться как-бы между рабочими столами… новыми и новыми.
Конечно-же, у этого способа есть свои ограничения, такие как один и тот-же фон рабочего стола и то-же количество и расположение системных и пользовательских иконок, НО… нам ведь никто не мешает добавить сохранение списков фонов, заставок, режимов монитора, иконок рабочих столов и прочее, и восстанавливать каждый раз их индивидуальные настройки.
Таким образом, уже можем определить основные требования к “виртуальному” столу:
- возможность получения списка хэндлов** всех видимых окон в системе
- сохранение (скрытие) и восстановление (визуализация) списка окон открытых для каждого режима
- интерфейс управления
- возможность переключения между рабочими столами по горячей клавише
** хэндл (handle) – идентификатор каждого окна (процесса) в системе, с помощью него можно получить управление для данного конкретного окна
Практика. Виртуальный рабочий стол
Итак, приступим к основной задаче. Для работы нам понадобится следующее:
- среда Borland Delphi 5-7 (компиляция проекта)
- среда исполнения win32 (98/NT/XP/различные эмуляторы, типа WinE)
Так как наше приложение не будет иметь никаких интерфейсных окон, то целесообразно все управление поместить в трей по иконке на каждый рабочий стол и реализовать код полностью на WinAPI. Рассмотрим основные моменты в алгоритме управления и взаимодействия.
Прежде всего, введем*** n-m- мерные массивы для хранения хэндлов списка окон каждого из ”виртуальных” рабочих столов: pid, pid1, pid2, pid3, pid4, pid5: array of array of integer; //массив массивов
*** это нужно для того, чтобы знать - какое из окон приложений в системе было открыто или свернуто в каждом из выбранных режимов
Переберем хэндлы всех видимых окон приложений (кроме свернутых в иконку трея):
получение списка открытых окон в системе
…
//---------------- текущие окна
procedure get_pid;
var wnd : hWnd;
buff: array [0..127] of Char;
begin
setlength(pid,0,0);
//
wnd:= GetWindow(Handle, gw_HWndFirst);
while wnd <> 0 do begin
if (wnd <> Handle) and //свое
IsWindowVisible(wnd) and //невидимые
(GetWindow(wnd, gw_Owner) = 0) and //дочерние
(GetWindowText(wnd, buff, sizeof(buff)) <> 0)
then begin
GetWindowText(wnd, buff, sizeof(buff));
if lowercase(strpas(buff))<>'program manager' then begin
//отсекаем окно панели программ
setlength(pid,length(pid)+1,length(pid)+1);
pid[length(pid)-1,0]:= wnd;
pid[length(pid)-1,1]:= longint(IsIconic(wnd))
end
end;
wnd:= GetWindow(wnd, gw_hWndNext)
end
end;
…
Логику управления и переключения desktop-ов достаточно просто реализовать, зная текущий выбор пользователя из меню (или по горячей клавише) и восстанавливая при этом предыдущий сохраненный для этого состояния список окон:
основной алгоритм переключения виртуальных столов
…
//триггер столов-
procedure select(n: smallint);
var i: integer;
begin
glw:= n; //синхронизация
if glw_temp=n then exit;
//
case n of //управление иконками в режимах
0: begin set_tn(2,1,0,hint); set_tn(2,1,1,hint); set_tn(2,1,2,hint);
set_tn(2,1,3,hint); set_tn(0,1,4,hint) end; //12220
1: begin set_tn(2,1,0,hint); set_tn(2,1,1,hint); set_tn(2,1,2,hint);
set_tn(0,1,3,hint); set_tn(1,1,4,hint) end; //12202
2: begin set_tn(2,1,0,hint); set_tn(2,1,1,hint); set_tn(0,1,2,hint);
set_tn(2,1,3,hint); set_tn(1,1,4,hint) end; //12022
3: begin set_tn(2,1,0,hint); set_tn(0,1,1,hint); set_tn(2,1,2,hint);
set_tn(2,1,3,hint); set_tn(1,1,4,hint) end; //10222
4: begin set_tn(0,1,0,hint); set_tn(2,1,1,hint); set_tn(2,1,2,hint);
set_tn(2,1,3,hint); set_tn(1,1,4,hint) end //02222
end;
//скрыть текущие-
get_pid;
if n<>-1 then
for i:=0 to length(pid)-1 do begin
sendMessage(pid[i,0],WM_SYSCOMMAND,SC_MINIMIZE,0);
showwindow(pid[i,0],sw_hide)
end;
//восстановить предыдущие-
case n of
0: for i:=length(pid1)-1 downto 0 do begin
showwindow(pid1[i,0],SW_SHOW);
if pid1[i,1]=0 then
sendMessage(pid1[i,0],WM_SYSCOMMAND,SC_RESTORE,0)
end;
1: for i:=length(pid2)-1 downto 0 do begin
showwindow(pid2[i,0],SW_SHOW);
if pid2[i,1]=0 then
sendMessage(pid2[i,0],WM_SYSCOMMAND,SC_RESTORE,0)
end;
2: for i:=length(pid3)-1 downto 0 do begin
showwindow(pid3[i,0],SW_SHOW);
if pid3[i,1]=0 then
sendMessage(pid3[i,0],WM_SYSCOMMAND,SC_RESTORE,0)
end;
3: for i:=length(pid4)-1 downto 0 do begin
showwindow(pid4[i,0],SW_SHOW);
if pid4[i,1]=0 then
sendMessage(pid4[i,0],WM_SYSCOMMAND,SC_RESTORE,0);
end;
4: for i:=length(pid5)-1 downto 0 do begin
showwindow(pid5[i,0],SW_SHOW);
if pid5[i,1]=0 then
sendMessage(pid5[i,0],WM_SYSCOMMAND,SC_RESTORE,0)
end;
-1: begin //все-
for i:=length(pid1)-1 downto 0 do begin
showwindow(pid1[i,0],SW_SHOW);
sendMessage(pid1[i,0],WM_SYSCOMMAND,SC_RESTORE,0)
end;
for i:=length(pid2)-1 downto 0 do begin
showwindow(pid2[i,0],SW_SHOW);
sendMessage(pid2[i,0],WM_SYSCOMMAND,SC_RESTORE,0)
end;
for i:=length(pid3)-1 downto 0 do begin
showwindow(pid3[i,0],SW_SHOW);
sendMessage(pid3[i,0],WM_SYSCOMMAND,SC_RESTORE,0)
end;
for i:=length(pid4)-1 downto 0 do begin
showwindow(pid4[i,0],SW_SHOW);
sendMessage(pid4[i,0],WM_SYSCOMMAND,SC_RESTORE,0)
end;
for i:=length(pid5)-1 downto 0 do begin
showwindow(pid5[i,0],SW_SHOW);
sendMessage(pid5[i,0],WM_SYSCOMMAND,SC_RESTORE,0)
end;
end
end;
…
Сохраняем в каждом из массивов хэндлы и состояние окон (свернуто-развернуто) при смене режима:
обновляем список окон при переходе для каждого режима
…
if glw_temp=0 then begin
//сохранить текущий- 1
setlength(pid1,0,0);
for i:=0 to length(pid)-1 do begin
setlength(pid1,length(pid1)+1,length(pid1)+1);
pid1[length(pid1)-1,0]:= pid[i,0];
pid1[length(pid1)-1,1]:= pid[i,1]
end
end;
…
Реализация управления через трей и горячую клавишу:
обработка событий нажатий в трее и горячей клавишы
…
//обработка прерываний-
function WndProc(hnd, wmsg, wparam, lparam: integer): integer; stdcall;
procedure down(lparam,n: integer);
begin
case lparam of
WM_LBUTTONdblCLK: ;
WM_MBUTTONDOWN : exito;
WM_LBUTTONDOWN : ;
WM_LBUTTONUP : select(n); //переключение-
WM_RBUTTONUP : begin
SetForegroundWindow(hnd);
if (PopupMenu<>0) then MakeTrayPopup(lparam)
end;
end
end;
var n : Integer;
TrayPopup : Boolean;
begin
TrayPopup:= PopupMenu<>0;
//
case wmsg of
//переключение между столами-
WM_HOTKEY: begin
inc(glw); if glw>4 then glw:= 0;
select(glw)
end;
WM_COMMAND : If (TrayPopup) then for n:= 0 to MItemCount-1 do
if MenuKeys[n]=lparam then TrayProc(dword(wparam));
//обработка каждого callback-
WM_USER + 222+0: down(dword(lparam),4); //нажатия на 1-ю иконку
WM_USER + 222+1: down(dword(lparam),3); //…2
WM_USER + 222+2: down(dword(lparam),2); //…3
WM_USER + 222+3: down(dword(lparam),1); //…4
WM_USER + 222+4: down(dword(lparam),0); //…5
WM_DESTROY: exito;
else Result:= DefWindowProc(hnd, wmsg, wparam, lparam)
end
end;
…
И конечно-же, отображение управляющей части в трее:
создание и отображение n- иконок в трее
…
//=======
//модуль ресурсных иконок + hint
procedure set_tn(ti,r,n: smallint; hint:shortstring);
begin
IconData.cbSize:= SizeOf(IconData);
IconData.Wnd := Handle;
//
IconData.uID := n;//n- иконка
IconData.uFlags:= NIF_ICON or NIF_MESSAGE or NIF_TIP;
IconData.uCallbackMessage:= WM_USER + 222+n; //TRAY_CALLBACK; //n- callback
case ti of
0: IconData.hIcon:= loadicon(HInstance,'pn');
1: IconData.hIcon:= loadicon(HInstance,'pm');
2: IconData.hIcon:= loadicon(HInstance,'pw')
end;
StrPLCopy(IconData.szTip,hint+' '+inttostr(4-n+1),SizeOf(IconData.szTip)-1);
if r=0 then begin //-add
Shell_NotifyIcon(NIM_ADD, @IconData);
tmr1:= timesetevent(250,0,@Ontmr1,0,TIME_PERIODIC)
end;
if r=1 then Shell_NotifyIcon(NIM_MODIFY, @IconData);//-mod
if r=2 then begin //-del
timeKillEvent(tmr1);
//DeallocateHWnd(IconData.Wnd);
Shell_NotifyIcon(NIM_DELETE, @IconData)
end
end;
…
В результате компиляции проекта, в трее появятся 5- зеленых иконок нашего приложения (см. рис.2):

Рис. 2. Выбор режимов рабочего стола
Вы можете ограничить количество отбражаемых иконок путем исключения лишних процедур set_tn при создании приложения. Каждая из иконок отвечает за соответствующий рабочий стол. При этом смена режима возможна как циклически по горячей клавише <Win>+<1>, так и по выбору пользователя щелчком левой кнопкой мыши по иконкам, либо правой кнопкой через меню.
Заключение
Полные исходные тексты, компиляцию и ресурсы проекта виртуального рабочего стола (файл vd.zip) вы можете скачать с сайта автора http://raxp.radioliga.com
Полный вариант статьи опубликован в [3]
Ресурсы
Контактная информация:
raxp@mail.zp.ua
31.01.2009
[Переход к списку статей]
|