Visual Basic.Net
Воскресенье, 20.07.2025, 23:11
Меню сайта

Категории каталога
Visual Basic.NET [9]
Visual Basic 6.0 [17]
VBA [13]
VBScript [1]

Форма входа

Поиск

Друзья сайта
Создайте свой сайт Все для веб-мастера Программы для всех Мир развлечений WOlist.ru - каталог качественных сайтов Рунета

Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Наш опрос
Оцените мой сайт
1. Отлично
2. Неплохо
3. Хорошо
4. Плохо
5. Ужасно
Всего ответов: 23

Главная » Статьи » Программирование на Visual Basic » Visual Basic.NET

Объект My

Настройка пространства имен My

Пространство имен My в Visual Basic 2005 облегчает создание впечатляющих приложений. Благодаря API, основанному на задачах, интуитивно-понятной иерархии и инфраструктуре приложений вы можете задействовать всю мощь Microsoft .NET Framework, и зачастую для решения весьма сложной задачи достаточно всего одной строки кода. API пространства имен My построено на полностью расширяемой архитектуре, что позволяет изменять функциональность пространства имен My и добавлять новые сервисы к его иерархии, чтобы адаптировать его под требования конкретного приложения.

Пространство имен My предоставляет свойства и методы, которые «просто работают». При решении распространенных задач программирования его сервисы уменьшают потребность в освоении всей .NET Framework. В отличие от многих типов и методов этой инфраструктуры пространство имен My «заточено» на простые решения частых проблем. Однако, если у вас другие требования, отказываться от него незачем. Применяя модель адаптации My, вы можете изменять поведение его членов так, как это нужно в вашем приложении.

Конфигурирование существующих членов пространства имен My

Одна из серьезнейших проблем, с которыми сталкиваются разработчики при использовании универсальных инфраструктур, — задача конфигурирования. Поэтому члены пространства имен My поддерживают конфигурацию по умолчанию, подходящую в большинстве случаев. Так, пространство имен System.Security содержит мощную, свободно расширяемую инфраструктуру для построения защиты на основе ролей. Однако обычная задача, скажем, настройка классов, нужных для выполнения от имени текущего пользователя Windows, может вызвать затруднения:

System.Threading.Thread.CurrentPrincipal = _
 New System.Security.Principal.WindowsPrincipal( _
 System.Security.Principal.WindowsIdentity.GetCurrent)

My.User по умолчанию инкапсулирует текущего пользователя Windows, и инициализирующий код становится не нужен. Но конфигурацию My.User по умолчанию, как и многих другие членов пространства My, при необходимости можно изменить:

My.User.CurrentPrincipal = _
 New WindowsPrincipal(WindowsIdentity.GetAnonymous())

Изменяя свойство CurrentPrincipal в My.User, вы меняете пользователя, от имени которого будет выполняться приложение. В свою очередь My.User возвращает информацию о заданном пользователе.

My.User логически инкапсулирует пользователя, который в данный момент вошел в систему. По умолчанию My.User настроен на текущего пользователя Windows. Но выполнение от имени пользователя Windows не всегда уместно: иногда для получения информации о пользователе приходится полагаться на внешнюю инфраструктуру, например на базу данных SQL. Применяя инфраструктуру System.Security.Principal, My.User можно настроить на использование реализаций IPrincipal и IIdentity, предназначенных для взаимодействия с альтернативными моделями аутентификации и авторизации:

My.User.CurrentPrincipal = CustomPrincipal

После того как задано My.User.CurrentPrincipal, при последующих вызовах My.User будет запрашивать вашу реализацию проверки имени пользователя, его ролей и т. д. Разработка собственных реализаций IPrincipal и IIdentity выходит за рамки этой статьи.

Настройка My.Application.Log

Во многих приложениях нужна детальная трассировочная информация для наблюдения за его состоянием и аудита. My.Application.Log содержит простые методы для записи трассировочной информации различных категорий в файл журнала:

Private Sub MyApplication_UnhandledException( _
 ByVal sender As Object, ByVal e _
 As UnhandledExceptionEventArgs) _
 Handles Me.UnhandledException
 My.Application.Log.WriteException(e, _
 TraceEventType.Critical, "The application has crashed _
 due to an unhandled exception")
End Sub

Так как My.Application.Log опирается на инфраструктуру System.Diagnostics.TraceSource, его параметры по умолчанию можно изменять, а также задавать направление и фильтрацию сообщений через файл .config. Например, зачастую требуется записывать критические сообщения об ошибках в Application Event Log, чтобы системные администраторы могли их обнаружить. Для перенаправления всех сообщений в файл (а критических — в Application Event Log) добавьте в файл App.config код, приведенный в листинге 1.

Применяя модель расширения, содержащуюся в пространстве имен My, можно добавлять к нему новые члены.

Листинг 1. Распределение трассировочных сообщений

 <system.diagnostics>
 <sources>
 <!-- DefaultSource используется My.Application.Log
 для записи сообщений -->
 <source name="DefaultSource" switchName="DefaultSwitch">

 <!-- Секция listeners определяет, куда записывать
 сообщения. Я задаю файл и журнал событий. -->
 <listeners>
 <add name="FileLog"/>
 <add name="EventLog"/>
 </listeners>

 </source>
 </sources>
 <!-- Секция switches определяет, когда TraceSource включен
 или выключен. Здесь я указываю, что My.Application.Log
 включен для всех сообщений с уровнем значимости
 большим или равным уровню Information. -->
 <switches>
 <add name="DefaultSwitch" value="Information" />
 </switches>

 <!-- Слушатели записывают сообщения в журнал. -->
 <sharedListeners>
 <add name="FileLog" type=
 "Microsoft.VisualBasic.Logging.FileLogTraceListener"
 initializeData="FileLogWriter"/>
 <add name="EventLog" type=
 "System.Diagnostics.EventLogTraceListener"
 initializeData="My Customization Example">

 <!-- Добавив фильтр, я настроил слушатель на запись
 лишь подмножества сообщений. Здесь в Event Log
 добавляются только критические сообщения.-->
 <filter type="System.Diagnostics.EventTypeFilter"
 initializeData="Critical"/>

 </add>
 </sharedListeners>
</system.diagnostics>


My.Application.Log управляется файлом .config, и у него есть дополнительное преимущество — его можно изменить после развертывания приложения, так как перекомпиляция не потребуется.

Настройка My.Computer.Network

My.Computer.Network содержит простые методы для определения доступности сети, а также для закачивания и скачивания файлов:

If My.Computer.Network.IsAvailable Then
 My.Computer.Network.DownloadFile( _
 "http://myserver.com/log.txt", _
 "c:\archives\serverlog.txt")
End If

My.Computer.Network опирается на классы System.Net и наследует их конфигурацию периода выполнения. Например, администраторы могут добавить следующий раздел в конфигурационный файл компьютера или приложения и задать прокси-сервер по умолчанию для всех сетевых запросов:

<system.net>
 <defaultProxy>
 <proxy usesystemdefault="true"/>
 </defaultProxy>
</system.net>

В свою очередь, My.Computer.Network автоматически определит эти параметры и применит их.

Настройка My.Settings

My.Settings инкапсулирует мощную инфраструктуру для управления параметрами приложения вроде адресов Web-сервисов и пользовательских настроек. My.Settings позволяет добавлять и удалять параметры на этапе разработки и обращаться к ним в период выполнения без написания утомительного и чреватого ошибками кода для поиска, разбора и обновления конфигурационных файлов. Так как многим приложениям требуется хранение параметров в других местах (например в реестре Windows или в INI-файлах), можно реализовать собственную логику, определяющую, откуда и как My.Settings читает параметры. В конфигурационной модели My.Settings применен проектировочный шаблон Provider, широко используемый в ASP.NET 2.0 Settings Provider реализует логику чтения и записи параметров в конкретной среде, например в Web-сервисе или реестре Windows. Settings Provider можно указывать для каждого параметра отдельно или сразу для всех в My.Settings.

Модель расширения пространства имен My упрощает и делает более согласованным процесс совместного использования решений разработчиками.

В Visual Studio 2005 провайдеры Settings Provider можно указать в сетке свойств для параметров. Для задания провайдера служит формат Namespace.Type.Assembly. На рис. 1 показан пример настройки My.Settings на использование RegistrySettingsProvider, включенный в исходный код к этой статье.

Применение RegistrySettingsProvider Рис. 1. Применение RegistrySettingsProvider

Расширение пространства имен My

Пространство имен My обеспечивает решение многих распространенных задач программирования, но при работе в специфических областях могут встречаться проблемы, не решаемые им. Так, My.Computer поддерживает чтение и запись в последовательный порт, но не поддерживает USB-порты; My.Application содержит функциональность трассировки, но не предоставляет средств отчета о крахе; программы, активно работающие с данными, могут пользоваться узлом «My.Data» верхнего уровня для выполнения рутинных операций с данными. Применяя модель расширения, содержащуюся в пространстве имен My, можно добавлять к нему новые члены. Передача расширений пространства имен My другим разработчикам в той же предметной области позволит им использовать расширения так же легко, как и остальное содержимое пространства My.

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

В отличие от других пространств имен .NET Framework, основанных на внешних сборках (DLL), пространство My находится в корневом пространстве имен проекта Visual Basic и размещается в файле в памяти. В листинге 2 показана часть его содержимого.

Листинг 2. Обзор пространства имен My
 
 WindowsApplication1

Namespace My
 Module MyProject
 Public ReadOnly Property Application() As MyApplication
 Public ReadOnly Property Computer() As MyComputer
 Public ReadOnly Property User() As User
 ...
 End Module

 Partial Class MyApplication
 Inherits ApplicationServices.WindowsFormsApplicationBase
 End Class
 Partial Class My Computer
 Inherits Devices.Computer
 End Class
End Namespace

Microsoft.VisualBasic.dll

Namespace Devices
 Public Class Computer
 Public ReadOnly Property Ports() As Ports
 ...
 End Class
End Namespace

Namespace ApplicationServices
 Public Class WindowsFormsApplicationBase
 Public ReadOnly Property Info() As AssemblyInfo
 ...
 End Class
End Namespace

Свойства Application, Computer и User определены как My.Application, My.Computer и My.User соответственно. Что интересно, My.Application и My.Computer на самом деле возвращают экземпляры классов MyApplication и MyComputer, тоже определенных в пространстве My. Эти классы и тот факт, что они объявлены с ключевым словом Partial, играют важнейшую роль в расширении My.Application и My.Computer.

Добавление членов

Так как My — это лишь пространство имен, добавить свойства верхнего уровня относительно легко. Создайте в проекте новый модуль и поместите его в пространство имен My. Затем пометьте этот модуль атрибутом HideModuleName. Последний указывает IntelliSense скрыть имя модуля от пользователей, просматривающих пространство My.

Наконец, добавьте к модулю необходимые свойства. Для каждого такого свойства добавьте сопутствующее поле типа ThreadSafeObjectProvider(Of T). Оно применяется для создания безопасных в многопоточной среде экземпляров внутри свойства, т. е. каждый поток, обращающийся к расширенному свойству, получает свой экземпляр возвращенного типа. В следующем примере к пространству имен My добавляется расширение SampleExtension:

Namespace My
 <HideModuleName()> _
 Module MyProject
 Private m_extension As _
 New ThreadSafeObjectProvider(Of SampleExtension)
 Public ReadOnly Property SampleExtension() _
 As SampleExtension
 Get
 Return m_extension.GetInstance()
 End Get
 End Property
 End Module
End Namespace

Так как типы, возвращаемые My.Application и My.Computer, определены в проекте как Partial, их расширение — задача, решаемая достаточно прямолинейно. Сначала добавьте дружественный класс MyComputer, для которого укажите ключевое слово Partial. Поместите новый класс MyComputer в пространство имен My и добавьте к нему свойства и методы. Добавляемые свойства и методы сразу же становятся доступны из My.Computer:

Imports System.Net.NetworkInformation
Namespace My
 Partial Class MyComputer
 Public ReadOnly Property IPAddresses() _
 As IPAddressCollection
 ...
 End Property
 End Class
End Namespace

Расширение My.Application аналогично. Единственное отличие - имя Partial-класса (MyApplication).

Расширение модели событий My.Application

My.Application в проектах Windows Forms поддерживает набор событий, инкапсулирующих изменения в общем состоянии приложения, например его запуск и закрытие, а также изменения в соединении с сетью и сбои в приложении. Базовый класс Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase, наследником которого является My.Application, содержит входные точки для расширения; их можно использовать для выполнения своих действий до и после срабатывания событий. Вот как вызываются эти методы.

  • OnInitialize. Первая вызываемая входная точка. Здесь можно обработать аргументы командной строки. Если OnInitialize возвращает False, последовательность событий прерывается и приложение завершается.
  • OnCreateSplashScreen. Вызывается по окончании инициализации. Здесь задается экран-заставка (splash screen), показываемая во время загрузки приложения.
  • OnStartup. Вызывается непосредственно перед событием Startup. Если вернуть из этого метода False, последовательность событий прервется и приложение завершится.
  • OnRun. Вызывается после обработки события Startup и до отображения главной формы.
  • OnCreateMainForm. Последние из событий запуска приложения. Здесь задается My.Application-свойство MainForm.
  • OnShutdown. Вызывается непосредственно перед событием Shutdown.

В листинге 3 показано, как расширить модель событий My.Application, добавив функциональность для вывода экрана-заставки на время, заданное аргументом командной строки.

Листинг 3. Расширение модели событий MyApplication

 Class MyApplication
 Private m_splashTimeout As Integer = 2000
 Private m_timerThread As Thread

 ''' <summary>
 ''' Извлечь время отображения экрана-заставки
 ''' из аргументов командной строки
 ''' </summary>
 Protected Overrides Function OnInitialize( _
 ByVal commandLineArgs As ReadOnlyCollection(Of String)) _
 As Boolean
 For Each argument As String In commandLineArgs
 If (argument.Contains("/splashtimeout")) Then
 m_splashTimeout = Integer.Parse( _
 argument.Split(":")(1)) * 1000
 End If
 m_timerThread = New Thread(AddressOf RunTimer)
 m_timerThread.IsBackground = True
 m_timerThread.Start()
 Next
 Return MyBase.OnInitialize(commandLineArgs)
 End Function

 ''' <summary>
 ''' До отображения главной формы убедиться, что
 ''' экран-заставка появилась на требуемое время
 ''' </summary>
 ''' <remarks></remarks >
 Protected Overrides Sub OnRun()
 If SplashScreen IsNot Nothing AndAlso _
 SplashScreen.Visible = True AndAlso _
 m_timerThread IsNot Nothing AndAlso _
 m_timerThread.IsAlive Then
 m_timerThread.Join(m_splashTimeout)
 End If
 MyBase.OnRun()
 End Sub

 ''' <summary>
 ''' Этот метод выполняется примерно
 ''' m_splashTimeout миллисекунд
 ''' </summary>
 Private Sub RunTimer()
 Dim runningTime As Integer = 0
 While runningTime < m_splashTimeout
 Thread.Sleep(100)
 runningTime += 100
 End While
 End Sub
End Class

Члены верхнего уровня можно не только добавлять к пространству My, но и удалять или переопределять. Скажем, если в конкретном домене приложения требуется объект приложения типа, отличного от исходного Microsoft.VisualBasic.ApplicationServices.ApplicationBase, то My.Application можно переопределить так, чтобы тот возвращал более подходящий объект приложения. Вы также можете удалить, например, член My.WebServices из пространства имен My, если Web-сервисы вам не нужны.

MyType

Конструкция MyType сообщает компилятору Visual Basic, какие члены верхнего уровня должны быть видимы в проекте данного типа. Так, My.Forms не слишком полезен для проекта Windows Service, поэтому он исключается из пространства имен My. При расширении My конструкция MyType позволяет изменить это пространство имен так, чтобы его пользователи получили полностью адаптированное решение. MyType — готовое определение, применяемое для условной компиляции членов My. В качестве примера рассмотрим код, используемый для формирования пространства имен My в проектах Windows Forms:

#If _MyType = "WindowsForms" Then
 ' My.Forms будет включен в пространство имен My
#Const _MYFORMS = True
 ' My.Application будет содержать специфические
 ' для Windows Forms события и члены
#Const _MYAPPLICATION = "WindowsForms"
 ' My.Computer будет содержать члены для Windows-клиентов
#Const _MYCOMPUTER = "Windows"
 ' My.User будет возвращать пользователя Windows
#Const _MYUSER = "Windows"
#ElseIf _MyType = "Console" Then
 ' ...
#End If

Когда компилятору Visual Basic передается нестандартный MyType, пространство имен My становится пустым, и вам нужно добавлять обратно требуемые члены. Добавление новых членов верхнего уровня к пространству имен My уже было описано в соответствующем разделе, а для возвращения предопределенных членов вроде My.Forms следует пользоваться константами компилятора.

С каждым предопределенным членом верхнего уровня пространства имен My связана константа компилятора. В частности, булева константа компилятора _MYFORMS определяет, должен ли быть включен «Forms». Все предопределенные члены верхнего уровня пространства имен My и их константы перечислены в табл. 1.

Табл. 1. Константы компиляции пространства имен My

Имя члена Константа компиляции Возможные значения Возвращает
My.Application_MYAPPLICATIONTYPE
WindowsApplicationServices.ApplicationBase
ConsoleApplicationServices.ConsoleApplicationBase
WindowsFormsApplicationServices.WindowsFormsApplicationBase
My.Computer_MYCOMPUTERTYPE
WindowsDevices.Computer
WebDevices.ServerComputer
My.Forms_MYFORMS
TrueMy.Forms включается
FalseMy.Forms исключается
My.User_MYUSERTYPE
WindowsApplicationServices.User
WebApplicationServices.WebUser
My.WebServicesr_MYWEBSERVICES
TrueMy.WebServices включается
FalseMy.WebServices исключается

Так как предопределенные члены верхнего уровня настраиваются через константы компилятора, эти значения нужно передать компилятору в аргументах командной строки. В Visual Studio константы компилятора задаются на странице Compile в Project Designer.

Переопределение My.User

Я продемонстрирую определение собственного MyType на примере My.User, возвращающего нестандартный тип ExtendedUser. Последний содержит вспомогательные методы Login и Logout для выполнения аутентификации. Начните с создания библиотеки классов и добавления к ней следующего класса:

Imports System.Security
Public Class ExtendedUser
 Inherits Microsoft.VisualBasic.ApplicationServices.User
 Public Function Login(ByVal username As String, _
 ByVal password As SecureString) As Boolean
 'TODO: выполнить логику входа пользователя
 End Function

 Public Sub Logout()
 'TODO: выполнить логику выхода пользователя
 End Sub
End Class

Затем создайте проект Windows Forms и добавьте к нему ссылку на эту библиотеку классов. Добавьте такое определение для My.User:

#If _MyType = "Custom" Then
Namespace My
 <HideModuleName()> _
 Module MyExtensionModule
 Private m_userInstanceProvider As New _
 ThreadSafeObjectProvider(Of ExtendedUser)
 Public ReadOnly Property User() As ExtendedUser
 Get
 Return m_userInstanceProvider.GetInstance()
 End Get
 End Property
 End Module
End Namespace
#End If

Присвойте _MyType значение «Custom» и включите предопределенные члены верхнего уровня пространства имен My, передав компилятору соответствующие аргументы. Ниже приведены аргументы для включения всех предопределенных членов, кроме только что определенного My.User:

MyType="Custom", _MYAPPLICATIONTYPE="WindowsForms",
_MyType
_MYCOMPUTERTYPE="Windows", _MYFORMS=True, MYWEBSERVICES=True
Теперь My.User будет возвращать ExtendedUser во всех проектах Windows Forms.

Настройка пространства имен My

При выборе стратегий разработки, развертывания и поддержки расширений пространства имен My, проще всего разделить задачу на две части: создание расширений пространства имен My и создание библиотеки классов. Расширение пространства имен My — это код, предназначенный для раскрытия вашей библиотеки классов в My, как показано выше, а библиотека классов — сборка (DLL), содержащая типы, раскрываемые в расширении.

Когда компилятору Visual Basic передается нестандартный MyType, пространство имен My становится пустым, и вам нужно добавлять обратно требуемые члены.

Рекомендации по разработке

Следующие рекомендации помогут свести к минимуму расходы на поддержку компонентов-расширений пространства имен My.

Включайте только логику расширения. Логика, включаемая в собственно расширение пространства имен My, должна содержать лишь «начинку», необходимую для раскрытия функциональности в этом пространстве имен. Так как исходный код этого компонента включается в проект, обновление расширения весьма дорогостоящая процедура, которой по возможности следует избегать.

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

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

Разработка библиотек классов для My

Как и в большинстве объектных моделей, некоторые проектировочные шаблоны работают в пространстве имен My, а некоторые — нет. Разрабатывая расширения My, придерживайтесь следующих принципов.

API без состояний. Методы пространства имен My должны предоставлять конечное решение конкретной задачи. Входные параметры должны принимать лишь данные, необходимые для выполнения задачи. Методы, которые полагаются на предыдущее состояние, в My обычно неуместны. В My.Computer.FileSystem есть хорошие примеры API без состояний, отлично работающих в контексте My.

Глобальные экземпляры. Единственное состояние, поддерживаемое в My, — глобальное для всего проекта. Так, My.Application.Info инкапсулирует состояние, общее для всего приложения.

Простые типы параметров. Самым простым методам, как правило, не нужны параметры сложных типов. Они должны принимать строки, элементарные типы и т. д.

Методы-фабрики. Экземпляры некоторых типов создать не так-то просто. Методы-фабрики в расширениях My облегчают применение таких типов. Пример хорошо работающего метода-фабрики — My.Computer.FileSystem.OpenTextFileReader. Так как существует несколько типов Stream, этот метод четко указывает, какой Stream нужно использовать и каким образом.

В этих рекомендациях нет общих правил разработки библиотек классов. Это рекомендации, предназначенные разработчикам, использующим Visual Basic и пространство имен My.

Упаковка и развертывание расширений

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

Файл шаблона Visual Studio (.vstemplate) инкапсулирует контент, который можно либо использовать как основу для новых проектов (шаблон проекта), либо добавить к существующему проекту (шаблон элемента). Упаковка расширений My в виде файлов шаблона позволяет бесшовно интегрировать их с Visual Studio и дает возможность пользователям добавлять расширения к проекту через диалоговые окна Add New Item и New Project. Преимущества этого подхода в том, что шаблоны Visual Studio можно устанавливать на уровне пользователя или на уровне компьютера. Пример расширения пространства имен My, упакованного в виде шаблона элемента Visual Studio, содержится в исходном коде, который можно скачать для этой статьи.

Заключение

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

Категория: Visual Basic.NET | Добавил: Vadim (12.02.2009)
Просмотров: 4381 | Рейтинг: 5.0/1 |
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Copyright MyCorp © 2025
Бесплатный конструктор сайтовuCoz