Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

Книги: [Классика] [Базы данных] [Internet/WWW] [Сети] [Программирование] [UNIX] [Windows] [Безопасность] [Графика] [Software Engineering] [ERP-системы] [Hardware]
     

Секреты поваров компьютерной кухни или ПК: решение проблем

Касперски К.

Издано: 2003, BHV
Твердый переплет, 560 стр..

Аннотация
Статьи из книги

[Заказать книгу в магазине "Мистраль"]

Секреты среды разработки Visual Studio

(статья была опубликована в журнале "Программист")

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

На самом же деле, меню - лишь верхушка айсберга, а большая часть функциональных возможностей многих приложений скрыта под водой. Продвинутые программные пакеты, такие как, например, Microsoft Visual Studio содержат тысячи команд, и, если бы все они были "втиснуты" в меню, его бы размеры выросли по меньшей мере до Луны. Вот и приходится помещать в меню лишь наиболее важные (с точки зрения разработчиков) команды, скрывая остальные от глаз пользователя.

Не стоит думать, что все скрытые команды - никому не нужный балласт. Среди них притаилось немало подлинных драгоценностей, значительно облегчающих жизнь программисту.

Данная глава посвящена малоизвестным, но чрезвычайно полезным секретам среды разработки Visual Studio 6.0. Поскольку, эта среда так же необъятна, как и мир, рассказать обо всех возможностях в рамках одной статьи просто невозможно. Поэтому, ограничимся лишь встроенным редактором текстов - важнейшим компонентом любой среды разработки.

Маленькое замечание мимоходом: если вы пришли на Windows из UNIX и вам ужасно недостает возможностей тех сред разработки – эта глава для вас! Все, что было в UNIX, есть и в Windows, только скрытно от посторонних глаз. Но мы-то с вами не посторонние, правда? :-)

Закладки

Visual Studio поддерживает два типа закладок. Одни перечисляются в диалоговом окне "Bookmark", доступном из меню "~Edit\Bookmarks", а другие отмечаются голубым квадратиком слева от "заложенной" строки (см. рис. 1.1). Причем первые и последние никак не связаны между собой - добавление закладки "голубого квадратика" не изменяет содержимого списка "Bookmark" и наоборот. Более того, управление закладками "голубых квадратиков" вообще не доступно из системы меню! (Правда, закладками можно управлять через панель инструментов "Edit", но по умолчанию она на экране не отображается). Чтобы установить такую закладку подведите курсор к соответствующей строке и нажмите "горячую" клавишу <Ctrl-F2>. Повторное нажатие удаляет закладку.

Для удаления всех закладок "голубых квадратиков" предусмотрена команда BookmarckClearAll, доступная через комбинацию клавиш <Shift-Ctrl-F2>. Закладки, назначенные через меню "Edit\Bookmarks" (<Alt-F2>) при этом не удаляются.

Для быстрого поиска закладок можно воспользоваться как листанием вперед: клавиша <F2> перемещает курсор к следующей закладке (при этом курсор не обязательно должен находится на закладке), так и листанием назад: нажатие клавиш <Shift-F2> перемещают курсор к предыдущей закладке.

Кстати, при контекстном поиске подстроки можно автоматически помечать все, содержащие ее строки, "голубыми" закладками. Для этого в диалоговом окне "Find" (<Ctrl-F>) вместо "ОК" нажмите кнопку "Mark All". Повторный поиск не удаляет старых закладок, но добавляет к ним новые. Причем, удалить закладки, пользуясь одной лишь системой меню, невозможно! Но мы-то с вами уже знаем, что такие закладки удаляются либо нажатием <CtrlF2>, либо нажатием <ShiftCtrlF2>.

Секреты закладок на этом не заканчиваются. Если вас раздражает, что для добавления новой Bookmark–закладки необходимо выполнять целый ряд операций (нажимать <Alt-F2>, вводить имя закладки, искать кнопку "Add"), - воспользуйтесь командой "BookmarkDrop(Epsilon)", делающий все это автоматически! По непонятным причинам, ей не соответствует никакая клавишная комбинация, поэтому ее приходится назначать самостоятельно. Это можно сделать, например, так. В меню "Tools" выберите пункт "Customize", в открывшемся диалоговом окне перейдите к закладке "Keyboard" и в ниспадающем боксе "Category" выберите категорию "Edit". Теперь в окне "Commands" найдите команду "BookmarkDrop(Epsilon)", переместите курсор в окно "Press new shortcut key" и нажмите комбинацию клавиш, которой вам будет удобно выполнять эту команду. Если эта комбинация уже используется - в поле "Currently assigned" появится название ее владельца, в противном случае - "unassigned". Для подтверждения назначения новой клавишной комбинации нажмите кнопку "Assign".

Теперь при нажатии вашей "горячей" клавиши в "Bookmark" будут автоматически добавляться закладки, нумеруемые в прядке возрастания от нуля до девяти. Если возникнет необходимость установить закладку с каким-нибудь конкретным номером, воспользуйтесь командами "BoolmarkDrop1 (Brief)", "BoolmarkDrop2 (Brief)" "BoolmarkDrop10 (Brief)". По умолчанию им так же не соответствует никакая клавишная комбинация, и вы должны назначить ее самостоятельно.

Очень полезна команда "BookmarkJumpToLast (Epsilon)", циклически пролистывающая Bookmark-закладки в порядке убывания их номеров. Ей, как и предыдущим командам, назначать "горячую" клавишу приходится самостоятельно.

Рис 1.1 Закладка "голубого квадратика"

Препроцессор

Каждый, кто работал с листингами (особенно чужими), знает: какую сумятицу вносят директивы условной компиляции. Допустим, встречается в тексте директива "#ifdef _A_". Как определить - какие строки программы относятся к ее телу, а какие нет? Теоретически в этом нет ничего сложного - достаточно найти директиву "#else" или "#endif", но как ее найти? Контекстный поиск здесь непригоден. Поскольку директивы условной компиляции могут быть вложенными, нет никаких гарантий, что ближайший найденный "#endif" или "#else" относится к той же самой директиве "#ifdef". Приходится пролистывать программу вручную, мысленно отслеживая все вложения и переходы. Однако это, во-первых, очень медленно, а во-вторых, так легко "прозевать" несколько директив, особенно если они разделены большим количеством строк.

К счастью, Visual Studio умеет трассировать директивы условной компиляции в обоих направлениях. Правда, по совершенно непонятым мотивам эта возможность не афишируется и вообще скрыта от посторонних глаз.

Если курсор находится в теле одной из ветвей директивы условной компиляции (т. е. либо ветви "#if #else", либо "#else #endif", либо "#if #endif"), то нажатием <CtrlK> мы переместимся в ее конец! Повторное нажатие приведет к переходу либо на следующую ветвь, либо (если текущая ветвь исчерпана) - на следующую вышележащую директиву. Во избежание путаницы обращайте внимание на строку статуса: переход с ветви на ветвь сопровождается сообщением: "Matching #ifdef..#endif found", а переход к вложенной директиве - "Enclosing #ifdef..#endif found". Соответственно, сообщение "No Enclosing #ifdef..#endif found" говорит о том, что ничего найти не удалось.

Для обратной трассировки (т.е. прохождению цепочки директив снизу вверх) нажмите <CtrlJ>.

Комбинации <ShiftCtrlK> и <ShiftCtrlJ> автоматически выделяют тело трассируемых директив, - это очень полезно, если его планируется копировать в буфер или вообще вырезать из программы.

Трассировка условных директив - это просто сказка, в которую беззаветно влюбляешься с первых же минут знакомства! Изучение SDK'шных файлов, таких, например, как WINNT.H без нее просто немыслимо!


#ifdef _A_    // ( если здесь нажать Ctrl-K, мы
              // переместимся в L1
// много строк текста
#ifdef _B_
// много строк текста
#else      // ( если здесь нажать Ctrl-K, мы
           // переместимcя в S1
// много строк текста
#endif      // ( S1
#else      // ( L1
// много строк текста
#endif

Листинг 27 Трассировка директив условной компиляции чрезвычайно облегчает изучение исходных текстов

Поиск

Команда "FindBackwardDlg" открывает диалоговое окно "Find", автоматически устанавливая обратное направление поиска строки. По умолчанию она не связана ни с какой "горячей" клавишей и назначить ее вы должны самостоятельно ("Tools ( Customize ( Keyboard ( Category Edit ( FindBackwardDlg").

Соответственно, команда "FindForwardDlg" открывает окно "Find", автоматически устанавливая прямое направление поиска. Штатный вызов диалога "Find" комбинацией <CtrlF> сохраняет последнее используемое направление.

Команды "FindRegExpr" и "FindRegExprPrev" открывают диалоговое окно "Find" автоматически устанавливая галочку "Поиск регулярных выражений", причем, первая из них задает прямое, а вторая - обратное направление поиска.

"Горячая" клавиша <F3> повторяет поиск предыдущей подстроки не вызывая диалог "Find", что намного быстрее.

Еще удобнее комбинация <CtrlF3>, которая ищет следующее вхождение выделенного текста. Т. е. вместо того, чтобы вводить искомую подстроку в диалог "Find" достаточно выделить ее и нажать <CtrlF3>. Соответственно, <ShiftCtrlF3> ищет следующее вхождение выделенного текста в обратном направлении.

Пара "горячих" клавиш <Ctrl]> и <CtrlShift]> перемещают курсор к следующей или предыдущей парной скобке соответственно. Это чрезвычайно полезно при разборе "монтроузных" выражений. Допустим, у нас имеется выражение "(((A) + (B)) + (C))" и необходимо найти пару второй слева скобке. Подводим к ней курсор, нажимаем <Ctrl]> и вот она, третья скобка справа! Соответственно, <ShiftCtrl]> возвратит нас на исходную позицию назад. Таким образом, проверка корректности вложения скобок из рутинного труда превращается в приятное развлечение.

Команды "LevelUp" и "LevelDown" очень похожи на предыдущие, но, во-первых, не требуют, чтобы курсор находился на скобке, а, во-вторых, не имеют собственных горячих клавиш. На мой взгляд, это несправедливо и нелогично, т.к. они намного удобнее в работе!

Команды "LevelCutToEnd" и "LevelCutToStart" вырезают в буфер обмена тело выражения до следующей или предыдущей парной скобки соответственно. Если же вам надо не вырезать, а копировать, то можно прибегнуть к небольшой хитрости - вырезать текст и тут же выполнить откат (Undo). Фокус в том, что откат не затрагивает буфер обмена, но восстанавливает удаленный текст. Как нетрудно догадаться, обе команды "горячими" клавишами не обременены, и назначать их придется самостоятельно.

Рис. 1.2 Поиск парных скобок

"Горячая" клавиша <CtrlD> перемещает курсор в "Find Tools" - ниспадающий бокс, расположенный на панели инструментов и сохраняющий несколько последних шаблонов поиска (см. рис. 3). Конечно, по нему можно кликнуть и мышкой, но клавиатура позволит сделать это быстрее, без отрыва рук от производства!

Рис. 1.3 Переход в окно "Find Tools"

Удаление текста

В первую очередь хотелось бы обратить внимание на команду "CutAppendNext", позволяющую добавлять вырезаемые фрагменты в конец буфера обмена без затирания его содержимого. По умолчанию она не связана ни с какой "горячей" клавишей и назначить ее вы должны самостоятельно ("Tools ( Customize ( Keyboard ( Category Edit ( CutAppendNext"). В работе с ней есть одна хитрость - сама CutAppendNext не вырезает текст в буфер, но говорит редактору, что следующая операция вырезания (Cutting) текста должна не перезаписывать (overwrite) содержимое буфера, а дописываться (append) к нему.

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

Ее сестра - команда "DeleteHorizontalSpace" удаляет все пробелы и символы табуляции, расположенные по обе стороны от курсора. Это бывает полезно, в частности, при удалении лишних отступов при переформатировании листинга. Как уже, вероятно, догадался читатель, назначать горячую клавишу на эту команду ему придется самостоятельно.

"Горячая" клавиша <CtrlL> вырезает в буфер обмена текущую строку целиком, а ее "сестра" - <ShiftCtrlL> просто удаляет строку, сохраняя содержимое буфера обмена неизменным.

Две других команды "LineDeleteToEnd" и "LineDeleteToStart" удаляют "хвост" строки справа и слева от курсора соответственно.

Операции со строками и словами

Клавишная комбинация <ShiftAltT> меняет местами текущую и предыдущую строку, что в некоторых ситуациях оказывается весьма сподручно. Ее ближайшая родственница <ShiftCtrlT> отличается лишь тем, что меняет местами не строки, а слова.

Пара команд "LineIndent" и "LineUnindent" сдвигают текущую строку на одну позицию табуляции назад и вперед соответственно. Какая от этого выгода? Не легче ли нажать <Tab> иди <Del>? Нет, не легче, и вот почему - указанные команды не требуют перемещать курсор в начало строки и выполняются из любой ее точки, что, согласитесь, ощутимо экономит время.

Команда "IndentToPrev" выравнивает текущую строку по образу и подобию предыдущей, что значительно упрощает форматирование листинга. Ее ближайшая родственница - "IndentSelectionToPrev", как и следует из ее названия, выравнивает сразу весь выделенный блок, что еще удобнее!

Перемещение по тексту

Команда "End(Brief)" циклически перемещается к концу текущей линии, нижней строке в окне и, наконец, последней строке текста. Возможность быстрого перемещения к нижней строке окна, действительно, очень удобна, поэтому, имеет смысл назначить этой команде свою "горячую" клавишу. ("Tools ( Customize ( Keyboard ( Category Edit ( End(Brief)").

Команда "Home(Brief)" очень похожа на предыдущую за тем исключением, что циклически перемещается не вниз, а вверх. По умолчанию ей так же не соответствует никакая "горячая" клавиша.

Комбинации <CtrlСтрелка вверх> и <CtrlСтрелка вниз> перемещают текст в окне соответственно вверх и вниз, сохраняя положение курсора, по крайней мере, до тех пор, пока он не достигнет последней строки окна. Это удобно при просмотре текста программы - чтобы увидеть следующую строку вам не обязательно через все окно гнать курсор вниз (вверх), как в обычном редакторе.

"Горячая" клавиша <F4> перемещает курсор к следующей строке, содержащий ошибку и отмечает ее черной стрелкой. Соответственно, <ShiftF4> перемещает курсор к предыдущей "ошибочной" строке.

Рис. 1.4 Навигация по ошибкам

Операции с сентенциями

"Горячая" клавиша <ShiftAltL> удаляет весь текст, расположенный справа от курсора до следующей сентенции (пустой строки). Если пустые строки вставлены в листинг "с умом", - эта операция может оказаться весьма полезной.

Команды "SentenceLeft" и "SentenceRight" перемещают курсор на следующую и предыдущую сентенцию соответственно.

Выделение

"Горячая" клавиша <F8> включает режим выделения текста, что равносильно удержанию <Shift>. Повторное нажатие на <F8> выключает режим выделения. Удобно при выделении больших блоков текста, - не затекает палец, ранее удерживающий <Shift>.

Еще интереснее комбинация <ShiftCtrlF8>, позволяющая выделять вертикальные блоки текста, что позволяет, в частности, одним махом удалить вертикальную строку комментариев.

Повтор действий

Для многократного выполнения некоторой операции, скажем, вставки тысячи символов "звездочки", абсолютно незачем кидать кирпичи на клавишу "*" - достаточно воспользоваться командой "SetRepeatCount", устанавливающей количество повторов следующей выполняемой операции. Количество повторов задается либо с цифровой клавиатуры, либо повторными вызовами "SetRepeatCount", при этом количество повторов будет каждый раз умножаться на четыре. Не правда ли здорово?!

Команды "SetRepeatCount0" "SetRepeatCount9" изменяют значение счетчика повторов от нуля от девяти соответственно.

Полезные макросы

Вместе с Visual Studio поставляется несколько образцов макросов, которые могут быть использованы не только для изучения Visual Basic'а, но и как самостоятельные утилиты. Нажмите <ShiftAltM>, затем в ниспадающем боксе "Macro File" выберите "SAMPLE" и в списке "Macro Name" появится список доступных макросов.

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


#ifndef __IDD_xxx_
#define __IDD_xxx_
  // Текст программы 
#endif //__IDD _xxx_

Листинг 28 Использования макроса OneTimeInclude для предотвращения повторного включения заголовочных файлов в программу

Весьма полезна и пара макросов "ifdefOut" и "ifndefOut", ограждающих выделенный текст директивами условной компиляции "#ifdef" и "#idndef" соответственно. Условие компиляции запрашивается автоматически в диалоговом окне. Вроде бы мелочь, а как экономит время!

Макрос "ToggleCommentStyle" меняет в выделенном блоке стиль комментариев с '//' на "/* */" и обратно, что чрезвычайно облегчает приведение всех листингов к единому стилю (особенно это полезно при работе в больших программистских коллективах - свой листинг вы оформляете так, как вам заблагорассудится, а потом просто переформатируете его - и все).

Макрос "PrintAllOpenDocument", как и следует из его названия, просто выводит все открытые активные документы на печать - при работе с большим количеством листингов эта возможность очень удобна.

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

Самостоятельная реализация функций с аргументами по умолчанию в классическом Си

Язык Си ++ выгодно отличается от своего предшественника тем, что поддерживает функции с аргументами по умолчанию. Для чего они нужны? Переставим себе такую ситуацию. Пусть у нас имеется активно используемая функция plot(int x, int y, int color), рисующая в заданных координатах точку заданного цвета. Допустим, в один прекрасный момент мы осознаем, что помимо координат и цвета нам жизненно необходим, скажем, атрибут прозрачности. Как быть? Реализовать еще одну функцию, практически повторяющую первую (ау, метод "copy-and-paste")? Не слишком-то удачное решение! Помимо неоправданного увеличения размеров программы возникнет проблема синхронизации обеих функций - при исправлении ошибок в первой функции, их придется "отлавливать" и во второй. (Вообще же, в грамотно спроектированной программе не должно присутствовать двух и более идентичных блоков).

Но почему бы просто не добавить еще один аргумент к функции? Дело в том, что это потребует внесения изменений во все остальные функции, использующие данную. А всякое изменение уже отлаженного кода чревато появлением ошибок в самых непредсказуемых местах. Еще хуже если такая функция вынесена в библиотеку - тогда ее модификация "срубит" множество программ, что весьма нежелательно.

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

К сожалению, подобная тактика не может быть непосредственно перенесена на Си, поскольку аргументов по умолчанию не он поддерживает. Правда, Си поддерживает функции с переменным числом аргументов, но это совсем не то, поскольку никакого контроля типов при этом не выполняется, что чревато трудноуловимыми ошибками.

Но выход все же есть! Автор предлагает на суд читателей свой любимый способ передачи аргументов функциям, разумеется, без претензий на право первооткрывателя. В общих чертах суть его в том, что гораздо выгоднее передавать не сами аргументы, а указатель на экземпляр структуры, содержащей в себе все необходимое функции данные.

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

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

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

Впрочем, при всех достоинствах этого метода, не лишен он и недостатков. Прежде всего, обращение к членам структуры, переданной по указателю, требует дополнительного расхода процессорного времени, что не самым лучшим образом сказывается на быстродействии. Впрочем, величина снижения производительности на практике не превышает 0,5% - 1% и ей вполне можно пренебречь. Другой минус - загромождение листинга структурами, что затрудняет его понимание. Однако к такому стилю очень быстро привыкаешь, и дискомфорт исчезает сам собой.

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

Конечно, данный метод, уже хотя бы в силу присущих ему недостатков, не претендует на радикальное изменение идеологии программирования и вовсе не намеривается вытеснять (и даже потеснясь) классический механизм передачи аргументов, но в некоторых случаях он все же бывает чрезвычайно полезен. Сам автор этой статьи использовал его в нескольких крупных проектах и полученным результатам остался доволен. Благодаря нему кардинальные нововведения новых версий и даже перенос из среды MS-DOS в операционную систему Windows обошлись практически без модификации уже отлаженного кода.

Аннотация
Статьи из книги

[Заказать книгу в магазине "Мистраль"]

VPS в России, Европе и США

Бесплатная поддержка и администрирование

Оплата российскими и международными картами

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...