RDM/2 The Russian Electronic Developer Magazine  
RDM/2 Русский электронный журнал разработчика  
ДомойОт редактораПишите намОбратная связьRU/2

Исследование модели SOM. Часть 3

PC Magazine/RE logo О системно-объектной модели SOM, на которой основана оболочка OS/2
часть 1, часть 2, часть 3
(С) СК Пресс 5/96
PC Magazine, January 9, 1995, стр. 279 Габриель Ганьон - менеджер в области информационных технологий (г. Бостон)

---

Создайте специализированные объекты SOM для своего "рабочего стола".

В третьей части обзора мы более подробно рассмотриваем различные управляющие последовательности начальной установки REXX, которые вы можете использовать для настройки своего "рабочего стола".

Если вы исследовали OS/2 Workplace Shell, то, возможно, знаете о неисчислимых параметрах настройки, предусмотренных в Блокноте установки (Settings Notebook) WPS-объектов. Каждый из этих параметров в свою очередь может быть сконфигурирован с помощью управляющих последовательностей начальной установки функций SysCreateObject и SysSetObjectData библиотеки RexxUtil. Таким образом, в дополнение к конфигурирированию объектов через "Блокнот установки" или с помощью техники drag-and-drop вы можете использовать для их настройки REXX. Во многих случаях оказывается быстрее воспользоваться методом непосредственных манипуляций вместо того, чтобы написать REXX-программы, но функции REXX незаменимы, когда вам требуется провести настройку большого числа рабочих станций. Настройка "рабочего стола" с помощью REXX также представляет собой эффективный способ обеспечения безопасности, так как вы можете восстановить утерянные или испорченные объекты, просто заново выполнив REXX-программу.

Создание специализированной инструментальной панели Launchpad

Предположим, что вы - сетевой администратор и вам нужно создать специализированные "Инструментальные панели" для каждого пользователя. Каждый подразделение хочет, чтобы наиболее часто используемые программы находились на линейке, отделенной от стандартной "Инструментальной панели" Launchpad, - плановый отдел, например, пользуется в основном инструментами дл планирования деятельности предприятия, а отдел маркетинга чаще использует электронные таблицы и текстовые процессоры.

К сожалению, OS/2 не имеет шаблона дл "Инструментальных панелей"; если вы хотите создать новую "Инструментальную панель" с помощью "Блокнота установки", вам нужно сделать копию стандартной "Инструментальной панели" и удалить все ее объекты (перетащить их в мусорную корзину или выбрать пункт Delete (удалить) из всплывающего меню объекта). Затем вы должны заполнить "Инструментальную панель", перетаскивая на нее новые программные объекты. Поскольку параметры "Инструментальной панели" не сохраняются в отдельном файле на диске (класс WPLaunchPad - потомок класса WPAbstract), вы не можете скопировать их на дискету для последующего распространения. Следовательно, вы должны повторять процедуру заново для каждого "рабочего стола". Создание специальной "Инструментальной панели" поначалу кажетс простым, но к моменту, когда вы закончите ее установку на десятой рабочей станции, у вас, возможно, появитс желание сменить место работы.

Идентификаторы объектов

Однако эту же задачу можно решить с использованием REXX. Во-первых, вам надо выяснить, какую прикладную программу хочет загрузить каждый отдел. Допустим, плановый отдел хочет поместить на новую "Инструментальную панель" несколько настольных инструментов OS/2: "Калькулятор", "Календарь", "Ежедневник" и "Ежемесячник", "Часы" и "Список неотложных дел". Отдел высказал пожелание, чтобы "Ежемесячник" был помещен в выдвижной ящик, расположенный над "Ежедневником", а "Часы" - над "Списком неотложных дел".

Чтобы получить доступ к каждому из этих объектов в среде REXX, вам нужно знать их идентификаторы или их маршрута доступа и имена файлов - если таковые имеются. Идентификаторы объектов предпочтительны, так как объекты могут быть перемещены. Идентификаторы объектов уникальны для каждого объекта и заключаются в угловые скобки:

   <objectname>
Всем стандартным объектам WPS назначаетс идентификаторы по умолчанию, и большинство из них начинаются с одних и тех же трех символов: WP_. Таким образом, вам следует избегать назначения объектам идентификаторов, начинающихся с этих трех символов.

Врезка "Используемые по умолчанию объекты Workplace Shell" показывает применяемые по умолчанию идентификаторы для большинства WPS-объектов, используемых в OS/2 Warp. Мы более подробно объясним механизм ссылок на идентификаторы ниже, а сейчас приведем идентификаторы, нужные для нашего примера:

В идентификаторах объектов регистры различаются: <WP_LAUNCHPAD> - не тот же самый объект, что и <WP_Launchpad>, поэтому будьте внимательны при их вводе, иначе может случиться, что WPS не найдет нужный вам объект.
Объекты Workplace Shell, используемые по умолчанию
Имя объектаИдентификатор объекта Класс объекта Местонахождение
16-цветная палитра <WP_LORESCLRPAL> WPColorPalette <WP_CONFIG>
256-цветная палитра <WP_HIRESCLRPAL> WPColorPalette <WP_CONFIG>
Сведения о прикладных программах <WP_APPLBK> WPProgram <WP_INFO>
Калькулятор <WP_DCALC> WPProgram <WP_TOOLS>
Календарь <WP_DCALEM> WPProgram <WP_TOOLS>
CD-плейер <MMPM_CDPLAYER1> WPProgram <MMPM2_FOLDER>
Программа просмотра буфера обмена <WP_CLIPV> WPProgram <WP_TOOLS>
Приглашения на ввод команды <WP_PROMPTS> WPFolder <WP_DESKTOP>
Командная ссылка <WP_CMDREF> WPProgram <WP_INFO>
Страна <WP_CNTRY> WPCountry <WP_CONFIG>
Ежедневник <WP_DIARY> WPProgram <WP_TOOLS>
"Рабочий стол" <WP_DESKTOP> WPDesktop <WP_DESKTOP>
Инсталляция драйвера устройства <WP_DDINST> WPProgram <WP_CONFIG>
Цифровой аудиоплейер <MMPM_DAPLAYER1> WPProgram <MMPM2_FOLDER>
DOS с накопителя A: <WP_DOS_DRV_A> WPProgram <WP_PROMPTS>
Полный экран DOS <WP_DOSFS> WPProgram <WP_PROMPTS>
Окно DOS <WP_DOSWIN> WPProgram <WP_PROMPTS>
Накопители <WP_DRIVES> WPDrives <WP_DESKTOP>
Двойная загрузка <WP_DBOOT> WPProgram <WP_PROMPTS>
Расширенный редактор <WP_EPM> WPProgram <WP_TOOLS>
Палитра шрифтов <WP_FNTFAL> WPFontPalette <WP_CONFIG>
Игры <WP_GAMES> WPFolder <WP_DESKTOP>
Глоссарий <WP_GLOSS> Mindex <WP_INFO>
Редактор пиктограмм <WP_ICON> WPProgram <WP_TOOLS>
Информация <WP_INFO> WPFolder <WP_DESKTOP>
Клавиатура <WP_KEYB> WPKeyboard <WP_CONFIG>
Инструментальная панель <WP_LAUNCHPAD> WPLaunchpad <WP_DESKTOP>
Игра Mah-jongg <MAH_EXE> WPProgram <MAH_FOLDER>
Mah-jongg солитер <MAH_FOLDER> WPFolder <WP_GAMES>
Служебный "рабочий стол" "WP_MAINT> WPDesktop <WP_DESKTOP>
Главный индекс подсказки <WP_MINDEX> Mindex <WP_INFO>
Музыкальный MIDI-плейер <MMPM_MIDIPLAYER1> WPProgram <MMPM2_FOLDER>
Переносимые прикладные программы <WP_MIGAPP> WPProgram <WP_CONFIG>
Программа просмотра минимизированных окон <WP_VIEWER> WPMinWinViewer <WP_DESKTOP>
MM-конвертер данных <MMPM2_MMCONVERTER> WPProgram <MMPM2_FOLDER>
Ежемесячник <WP_DMNTH> WPProgram <WP_TOOLS>
Мышь <WP_MOUSE> WPMouse <WP_CONFIG>
Папка мультимедиа <MMPM2_FOLDER> WPFolder <WP_DESKTOP>
Информация мультимедиа <WP_MULTIMBK> WPProgram <WP_INFO>
Установка мультимедиа <MMPM2_SETUP> WPProgram <MMPM2_FOLDER>
Сеть <WP_NETWORK> WPNetwork <WP_DESKTOP>
Нигде <WP_NOWHERE> WPFolder <WP_DESKTOP>
Шахматы OS/2 <WP_CHESS> WPProgram <WP_GAMES>
Полный экран OS2 <WP_OS2FS> WPProgram <WP_PROMPTS>
Система OS/2 <WP_OS2SYS> WPFolder <WP_DESKTOP>
Системный редактор OS/2 <WP_SYSED> WPProgram <WP_TOOLS>
Окно OS/2 <WP_OS2WIN> WPProgram <WP_PROMPTS>
PCMCIA Plug and Play <WP_CMPNP> WPCmpnp <WP_CONFIG>
Сведения о производительности <WP_PERFBK> WPProgram <WP_INFO>
Программа просмотра изображений <WP_PICV> WPProgram <WP_TOOLS>
Карта PM <WP_CHART> WPProgram <WP_TOOLS>
Управление энергопотреблением <WP_OWER> WPPower <WP_CONFIG>
Принтер <WP_PDVIEW> PDView <WP_DESKTOP>
Информация о печати <WP_PRINTBK> WPProgram <WP_INFO>
Производительность <WP_TOOLS> WPFolder <WP_DESKTOP>
Синхронизация <WP_PULSE> WPProgram <WP_TOOLS>
ReadMe <WP_RDME> WPShadow <WP_INFO>
Информация REXX <WP_REXREF> WPProgram <WP_INFO>
Схемная палитра <WP_SCHPAL28> WPSchemePalette <WP_CONFIG>
Поиск и сканирование файлов <WP_SEEK> WPProgram <WP_TOOLS>
Селективная инсталляция <WP_INST> WPProgram <WP_CONFIG>
Мусорная корзина <WP_SHRED> WPShredder <WP_DESKTOP>
Солитер-Клондайк <WP_KLDK> WPProgram <WP_GAMES>
Звук <WP_SOUND> WPSound <WP_CONFIG>
Спулер <WP_SPOOL> WPSpool <WP_CONFIG>
Электронные таблицы <WP_SPREAD> WPProgram <WP_TOOLS>
Начальный запуск <WP_START> WPStartup <WP_DESKTOP>
Система <WP_SYSTEM> WPSystem <WP_CONFIG>
Системные часы <WP_CLOCK> WPClock <WP_CONFIG>
Установка системы <WP_CONFIG> WPFolder <WP_DESKTOP>
Шаблоны <WP_TEMPS> WPTemplates <WP_DESKTOP>
Список неотложных дел <WP_TODO> WPProgram <WP_TOOLS>
Прикосновение <WP_TOUCH> WPTouch <WP_CONFIG>
Сведения о торговой марке <WP_TRADEMBK> WPProgram <WP_INFO>
Учебник <WP_TUTOR> WPProgram <WP_INFO>
Деинсталляция <WP_UNINST> WPProgram <WP_CONFIG>
Видеоплейер <MMPM2_SOFTWARE_
MOTION_VIDEO1>
WPProgram <MMPM2_FOLDER>
Регулятор громкости <MMPM2_MASTERVOLUME_D> WPProgram <MMPM2_FOLDER>
Полный экран WIN-OS2 <WP_WINFS> WPProgram <WP_PROMPTS>
Окно WIN-OS2 <WP_WIN2WIN> WPProgram <WP_PROMPTS>
Начальная установка WIN-OS2 <WP_WINCFG> WPWinConfig <WP_CONFIG>
Windows в OS/2 <WP_WINOS2BK> WPProgram <WP_INFO>

Функция SYSCREATEOBJECT

Теперь, когда мы располагаем нужными идентификаторами объектов, мы можем создать новую "Инструментальную панель" с помощью функции SysCreateObject библиотеки RexxUtil, которая позволяет создавать новые объекты и конфигурировать их. Она принимает пять параметров, в том числе несущие информацию о классе (Class), имени (Title) и местонахождении (Location) объекта, а также необязательные параметры Setup String (управляюща последовательность начальной установки) и Option Code (Код варианта). В случае если нужный вам объект уже существует, "Код варианта" обеспечивает выбор одного из вариантов: завершить выполнение функции неудачей (F) - это действие предпринимается по умолчанию, если вы не укажете конкретный Option Code; заменить существующий объект (R); или изменить существующий объект (U). В варианте изменения объект приобретает новые свойства, предоставленные управляющей последовательностью начальной установки.

Управляющая последовательность начальной установки - набор комбинаций "ключевое слово=значение", разделенных точками с запятой:

EXENAME=C:\MDOS\BASIC.COM;
STARTUPDIR=C:\MDOS;
PROGTYPE=VDM;
Ключевые слова представляют свойства объекта, которые меняются в зависимости от класса объекта и его места в иерархии WPS.

Существуют ключевые слова и необязательные параметры, позволяющие управлять любой характеристикой, которую вы можете задать из Блокнота установки и даже некоторыми недоступными оттуда. Они предлагают широкий набор функций, начиная от используемых для определени идентификатора объекта, и кончая такими, которые назначают тип сеанса и накладывают ограничения на возможность просмотра и удаления объекта.

Значительное число доступных параметров установки само по себе может привести в замешательство новичка, что, вероятно, и служит причиной того, что обща документация по языку REXX содержит лишь несколько примеров. Они слишком длинны, чтобы привести их здесь, но мы поместили в оперативную информационную службу PC Magazine Online файл, содержащий большинство параметров начальной установки, которые, по нашему мнению, могут пригодиться системному администратору. Они сгруппированы в таблицы по классам, начиная с WPObject. Поскольку свойства и поведение наследуются, установки WPObject передаются всем его подклассам. Для подклассов мы показываем только те установки, которые замещают или расширяют установки родительского класса. Иерархи классов WPS приведена в частях 1 и 2 нашей серии статей о SOM.

Создание инструментальной панели с помощью REXX

На лист. 5 показана программа REXX, служащая дл создания новой Инструментальной панели и размещения на ней перечисленных ранее прикладных программ. Заметьте, что мы назначаем переменную для управляющей последовательности начальной установки и связываем комбинации "ключевое слово=значение" с помощью символа ||. Это сделано главным образом для удобства чтения; вы можете вводить управляющую последовательность буквально такой, как она есть. Обратите также внимание на запятые, расставленные в конце некоторых строк. В языке REXX запятая в конце строки означает, что текст программы продолжается на следующей строке. Если вы разобьете программу на строки иначе, нежели в нашем примере, проверьте, что вы правильно рассставили запятые.
---

Лист.5. В этой части программы REXX делается попытка создания новой "Инструментальной панели" и показано, что из этого получилось
/* Создание новой "Инструментальной панели" на рабочем
столе и вывод результатов на экран */

/* загрузка функции RexxUtil */
call RXFuncAdd 'SysCreateObject', 'RexxUtil', 'SysCreateObject';

setupString = ,
  'OBJECTID=<OpsLaunchPad>;' ||,
  'FPOBJECTS=<WP_DCALC>,<WP_DCALEM>,<WP_DDIARY>,<WP_TODO>;' ||,
  'DRAWEROBJECTS=3,<WP_DMNTH,>;'

rc = SysCreateObject('WPLaunchPad', 'Operations LaunchPad',,
  '<WP_DESKTOP>', setupString );
  if rc = 1 then
    say 'Launchpad object created w/Rexx'
  else say 'Could not create Launchpad w/Rexx'

---

Ключевое слово FPOBJECTS добавляет новые объекты в правый конец горизонтально сконфигурированной палитры или на самый верх размещенной по вертикали палитры. В техническом отношении эта программа добавляет к "Инструментальной панели" команды быстрого вызова объектов, а не подлинные объекты. Но вам не нужно явно создавать команду быстрого вызова, так как "Инструментальная панель" делает это за вас. Синтаксис таков:

   FPOBJECTS=objectID1, objectID2, objectID3...;
Обратите внимание, что вы должны использовать запятые для разделения идентификаторов объектов и точку с запятой в конце управляющей последовательности начальной установки. Многоточие (...) указывает, что последовательность может продолжаться неопределенно долго. В нашем случае мы расширяем палитру слева направо: команда быстрого вызова "Калькулятора", "Календаря", "Ежедневника" и "Списка неотложных дел". (По умолчанию принимаются горизонтальные "Инструментальные панели".)

Посредством DRAWEROBJECTS добавляются объекты в выдвижной ящик над одним из элементов палитры. Выдвижной ящик изображается маленькой полоской; если в ящике содержатся какие-либо объекты, в середине полоски появляется метка. Принят следующий синтаксис:

   DRAWEROBJECTS=drawer_number, objectID1, objectID2...;
Выдвижные ящики идентифицируются по номерам и располагаются последовательно слева направо (или снизу вверх в вертикальной "Инструментальной панели"). В нашем примере палитра содержит четыре объекта, и "Ежедневник" находится в позиции 3. Так как мы хотели поместить "Ежемесячник" выше "Ежедневника", он попадает в выдвижной ящик 3. Обратите внимание, что запята отделяет номер ящика от идентификаторов объектов и идентификаторы объектов друг от друга. Вы можете поместить в ящик более одного объекта, но из-за особенностей синтаксиса конфигурировать можно лишь один выдвижной ящик за один раз.

Следовательно, лист. 5 не имеет элемента, который помещал бы "Часы" в ящик, находящийся над "Списком неотложных дел". Чтобы поместить объекты в дополнительные ящики, вам следует воспользоватьс функцией SysSetObjectData, изменив вновь созданные "Инструментальные панели".

Функция SYSSETOBJECTDATA

SysSetObjectData позволяет вам изменять параметры существующих объектов. Использовать ее намного проще, чем SysCreateObject, которая, будучи применена с параметром модификации (U) может служить для тех же целей. Синтаксис функции SysSetObjectData следующий:

   result=SysSetObjectData('NameofObject', 'Setup')
Переменная result представляет собой return-код. Функция передает в вызывающую программу 1, если она завершилась успешно, и 0 в случае неудачи. Если вы, например, попытаетесь модифицировать объект, которого не существует на "рабочем столе", то функция завершитс неудачей. NameofObject может быть либо идентификатором объекта, либо, в случае потомков класса WPFileSysytem, представлять собой путь доступа файловой системы к объекту, который вы хотите изменить. (Помните, что объекты, порожденные классом WPFileSysytem соответствуют каталогам и файлам. Любой другой тип объекта нуждается в идентификаторе объекта.) Setup - управляющая последовательность начальной установки, в которой используется тот же самый формат, что и в управляющей последовательности, применяемой функцией SysCreateObject.

Для настройки "рабочего стола" нам нужно еще добавить "Часы" в выдвижной ящик, расположенный выше "Списка неотложных дел" и, кроме того, внести некоторые другие изменения. Создав операционную "Инструментальную панель", мы обнаруживаем, что пиктограммы объектов почти неразличимы, и пользователи могут предпочесть иметь перед глазами описание каждого элемента, вместо одной лишь пиктограммы. На лист. 6 показано, как добиться этого с помощью функции SysSetObjectData. Обратите внимание, что свойствам LPTEXT и LPDRAWERTEXT присвоено значение YES (ДА), чтобы выводить под пиктограммами имена объектов. LPTEXT используется дл объектов палитры, а LPDRAWERTEXT - для объектов выдвижных ящиков.
---

Лист. 6. С помощью SysObjectData можно дополнить пиктограммы объектов описаниями каждого из элементов.
/* Внесение изменений в "Инструментальную панель" и
вывод результатов на экран */

/* Загрузка функции RexxUtil */
call RXFuncAdd 'SysObjectData', 'RexxUtil', 'SysObjectData';

setupString = ,
  'DRAWEROBJECTS=4,<WP_CLOCK>;' ||,
  'LPTEXT=YES;' ||,
  'LPDRAWERTEXT=YES;'

rc = SysObjectData('<OpsLaunchPad>', setupString );
  if rc = 1 then
    say 'Successfully added objects to LaunchPad'
  else say 'Could not added objects to LaunchPad'

---
Теперь, когда мы имеем готовые программы, можно скопировать их на все рабочие станции и работать с ними. Еще одна вещь, о которой нужно помнить, измен объекты: в большинстве случаев вам следует закрыть объект, прежде чем приступить к его модифицикации. Если вы будете проводить манипуляции с Инструментальными панелями LaunchPad, пока они открыты, то в их функционирование может быть внесена путаница. Чтобы привести их в порядок, просто закройте их и затем откройте вновь.

Распознавание идентификаторов объектов

И наконец, отдел маркетинга хочет поместить на свои "Инструментальные панели" Microsoft Word и Excel, вместе с некоторыми другими, менее известными пакетами. До сих пор во всех наших примерах мы либо назначали идентификаторы новым объектам, либо использовали идентификаторы по умолчанию для объектов, поставляемых вместе с OS/2; мы не работали с прикладными программами, которые добавлялись позднее. Существует несколько способов, позволяющих определить, каким идентификатором объекта следует воспользоваться.

Во-первых, для прикладной программы можно создать программный ссылочный объект и назначить ему идентификатор объекта. Создать программный ссылочный объект - то же самое, что перетащить шаблон Program на "рабочий стол" и сконфигурировать его. Вам нужно по меньшей мере знать местоположение EXE-файла программы. Возможно, вы также захотите знать, принимает ли он какие-либо параметры и каков его рабочий каталог. Эти необязательные величины устанавливаются с помощью ключевых слов EXENAME, PARAMETERS и STARTUPDIR соответственно.

На лист. 7 показан процесс создания программной ссылки для системного редактора System Editor OS/2. Мы расположили новый программный объект в папке "рабочего стола", но вы можете поместить его в любую папку по вашему выбору. Эта программа уже имеет ссылочный объект, но вы можете не знать его принимаемого по умолчанию идентификатора. Основным моментом при этом является то, что при создании новой программной ссылки надо назначить ей идентификатор объекта, чтобы знать, как обратиться к нему, когда он будет использоваться в других функциях.
---

Лист. 7. Если вам не известен идентификатор объекта прикладной программы, вы можете создать новую программную ссылку и назначить новый идентификатор.
/* Создание программного объекта на рабочем столе и
вывод результатов на экран */

/* Загрузка функции RexxUtil */
call RXFuncAdd 'SysCreateObject', 'RexxUtil', 'SysCreateObject';

setupString = ,
  'OBJECTID=<MyEditor>;' ||,
  'EXENAME=\OS2\E.EXE';

rc = SysCreateObject('WPProgram', 'System Editor',,
  '<WP_DESKTOP>', setupString );
  if rc = 1 then
    say 'Program object created w/Rexx'
  else say 'Could not createProgram w/Rexx'

---
Иной, более рискованный подход, заключается в проверке существующего списка идентификаторов объектов и последующей обоснованной догадке. Вы можете использовать функцию SysINI библиотеки RexxUtil, чтобы выяснить, какие идентификаторы объектов в настоящее время зарегистрированы в WPS. (Функция SysINI может также выполнять и некоторые другие задачи, но они не относятся к теме данной статьи.)

Образец программы, показанный на лист. 8, выводит на экран список активных идентификаторов объектов. Поскольку список будет длинным, вы, возможно, пожелаете сохранить результат в файле. В случае если вы не укажете идентификатор объекта, WPS назначает его самостоятельно, и некоторые из этих идентификаторов могут выглядеть весьма загадочно. Просматривая список, составленный нашей программой, мы обнаружили, что <WP_CWINWORDWINWORD> и <WP_CEXCELEXCELTAMICROSOF> - вероятные кандидаты для Microsoft Word и Excel. Попробовав их в подпрограмме REXX, мы обнаружили, что наши предположения были правильными.
---

Лист. 8. С помощью этой короткой REXX-программы вы можете вывести на экран список активных идентификаторов объектов.
/* Вывод на экран идентификатора объекта, известного
оболочке WPS */
call RXFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs';
call sysloadfuncs

result = SysIni( 'USER', 'PM_Workplace:Location', 'ALL:' 'ids')
if result \= 'ERROR:' then
  do i = 1 to ids.0
     say ids.i
  end

---
Для пакетов DOS и Windows поиск существующего идентификатора объекта может оказаться более предпочтительным решением, нежели начало работы с нуля, поскольку существующие объекты могли быть установлены через миграционную базу данных и, возможно, нуждаются в специальных установочных параметрах. Если же вы непременно желаете сделать все самостоятельно, то файл DATABASE.TXT в каталоге OS2/INSTALL содержит перечень всех установочных параметров, которые могут понадобиться для десятков программ DOS и Windows.

Исследование мира SOM

Мы надеемся, что этот цикл статей поможет вам составить представление о мощи и гибкости среды Workplace Shell, и побудит вас приступить к самостоятельному исследованию мира системной модели объектов SOM.

часть 1, часть 2

---
Интересные ссылки:

---

---
Комментариев к странице: 0 | Добавить комментарий
---
Редактор: Дмитрий Бан
Оформление: Евгений Кулешов


(C) Russian Underground/2