Бакалавр
Дипломные и курсовые на заказ

Система синхронизации каталогов устройств

КурсоваяПомощь в написанииУзнать стоимостьмоей работы

Для начала нужно создать синхропару. Это можно сделать из соответствующей панели — рисунок 4, появляющейся по нажатию кнопки «Добавить». В поля «Каталог А» и «Каталог Б» нужно ввести полный путь к синхронизируемым каталогам. Сделать это также можно с помощью диалогового окна выбора каталога, появляющегося по нажатию кнопки «Обзор…». Синхронизируемые каталоги должны быть различными и не являться… Читать ещё >

Система синхронизации каталогов устройств (реферат, курсовая, диплом, контрольная)

Министерство образования Республики Беларусь Учреждение образования

«Гомельский государственный университет имени Франциска Скорины»

Математический факультет Кафедра математических проблем управления Курсовая работа Система синхронизации каталогов устройств Исполнитель студент группы ПО-42 В. А. Мордвинов Гомель 2015

Реферат Ключевые слова: съемный диск, резервное копирование, файл, каталог, синхронизация.

Объект исследования: работа с файловой системой windows, язык C# и программная библиотека .net framework 4.0.

Методы исследования: среда разработки Microsoft Visual Studio 2013, win API.

Цель курсовой работы: проектирование и написание приложения для автоматизации процесса синхронизации каталогов устройств.

Выводы: было спроектировано и разработано приложение, автоматизирующее процесс синхронизации каталогов дисковых устройств, базирующееся на технологии .net framework 4.0.

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

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

Программа будет разрабатываться в среде разработки Visual Studio 2013 на языке C#. Данное средство предоставляет широкий инструментарий для разработки клиентских Windows Forms приложений, в том числе, для работы с файловой системой (управление и мониторинг изменений файлов и каталогов) и со сменными носителями (перехват событий подключения/отключения устройств).

Основная часть курсовой работы представлена четырьмя разделами:

1) описание назначения приложения;

2) краткий обзор использованных технологий;

3) схема диалога пользователя с разработанным приложением;

4) алгоритм синхронизации каталогов.

Первый раздел курсовой работы содержит подробное описание назначения приложения, а также схематическое описание логики работы приложения.

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

— win API;

— BackgroundWorker;

— FileSystemWatcher;

— пространство имен «Sysrem.IO» стандартной библиотеки классов языка c#.

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

Четвертый раздел описывает схему переключения состояний синхронизации:

— Analyzing (анализ состояния каталогов);

— Ok (каталоги синхронизированы);

— Off (синхронизация каталогов отключена);

— InProcess (идет процесс синхронизации);

— WaitSolution (ожидание выбора направления синхронизации пользоватлем);

— WaitProcess (в очереди на синхронизацию);

— NotAvailable (один из каталогов недоступен);

— UnSuccess (в процессе синхронизации произошла ошибка).

1. Назначение разработанного приложения Разработанное приложение предназначено для автоматизации процесса синхронизации каталогов.

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

Общий алгоритм работы программы можно представить в виде схемы, изображенной на рисунке 1.

Рисунок 1 — Общая логическая схема приложения

2. Использованные технологии и компоненты

2.1Пространство имен System. IO

Основной задачей приложения является копирование файлов. Следовательно, для работы с ними нужна специальная библиотека методов. В набор стандартных библиотек языка C# входит пространство имен «System.IO», предназначенное для работы с данными, в том числе, и с файлами. Оно и использовалось при разработке программы.

Классы, применяемые для просмотра файловой системы и выполнения таких операций, как перемещение, копирование и удаление файлов, показаны на рисунке 2.

Рисунок 2 — Иерархия классов для работы с файловой системой

В следующем списке объясняется предназначение всех этих классов:

— System. MarshalByRefObject — это базовый класс для всех классов .NET, позволяющих удаленное взаимодействие; разрешает маршализацию между доменами приложений;

— FileSystemlnfo — это базовый класс, представляющий любой объект файловой системы;

— Filelnfo и File — классы, представляющие файлы в файловой системе;

— Directorylnfo и Directory — классы, представляющие папки в файловой Системе;

— Path — класс содержит статические члены, используемые для манипулирования путевыми именами;

— Drive Info — класс включает свойства и методы, представляющие информацию о выбранном диске.

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

Directory и File включают только статические методы; экземпляры этих классов никогда не создаются. Эти классы используются с применением пути к соответствующему объекту файловой системы при каждом вызове методов-членов. Если нужно выполнить только одну операцию над файлом или папкой, то использование этих классов более эффективно, поскольку позволяет сэкономить за счет накладных расходов, связанных с созданием экземпляров классов .NET.

Directorylnfo и Filelnfo реализуют почти те же общедоступные методы, что и Directory и File, наряду с некоторыми общедоступными свойствами и конструкторами, но эти методы запоминают состояние, и члены этих классов не являются статическими. Перед тем, как ассоциировать экземпляры этих классов с определенной папкой или файлом, должны быть созданы экземпляры. Это значит, что данные классы более эффективны, когда нужно выполнять множество операций над соответствующим объектом файловой системы, не перечитывая каждый раз информацию о нем заново. В отличие от них, классы без состояния должны при каждом вызове метода всякий раз проверять все детали, касающиеся файла или папки.

Для сравнения двух этих типов классов, приведем пример кода копирования файла. С испольованием метода copy класса Filelnfo:

Filelnfo myFile = new Filelnfo («С:ProgramFilesMyProgramReadMe.txt»);

myFile.CopyTo (@" D: CopiesReadMe. txt");

И с использованием аналогичного метода класса File:

File.Copy (@" C: Program FilesMy ProgramReadMe. txt" ,

@" D: CopiesReadMe. txt");

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

Классы Directorylnfo и Filelnfo также позволяют получить сведения об элементе файловой системы, представленные в виде таблицы 1.

Над объектами файловой системы можно также выполнять действия, перечисленные в таблице 2.

Таблица 1 — Основные свойства классов Directorylnfo и Filelnfo

Имя

Описание

CreationTime

Время создания папки или файла.

DirectoryName (только для Filelnfo)

Полное путевое имя содержащей папки.

Parent (только для Directorylnfo)

Родительский каталог указанного подкаталога.

Exists

Существует ли файл или папка.

Extension

Расширение файла; пустое для папок.

FullName

Полное путевое имя файла или папки.

LastAccessTime

Время последнего доступа к файлу или папке.

LastWriteTime

Время последней модификации файла или папки.

Name

Имя файла или папки.

Root (только для Directoryinfо)

Корневая часть пути.

Length (только для Filelnfo)

Размер файла в байтах.

Таблица 2 — Основные методы классов Directorylnfo и Filelnfo

Имя

Назначение

Create ()

Создает папку или пустой файл с заданным именем. Для Filelnfo это также возвращает потоковый объект, позволяющий выполнять запись в файл.

Delete ()

Удаляет файл или папку. Для папок предусмотрена опция рекурсивного удаления вложений.

MoveTo ()

Перемещает и/или переименовывает файл или папку.

СоруТо () (Только для Filelnfo)

Копирует файл. Копирование дерева каталогов должно быть реализовано индивидуальным копированием каждого файла и создавать все папки, соответствующие исходным.

GetDirectories () (Только для Directorylnfo)

Возвращает массив объектов Directorylnfo, представляющий все папки, вложенные в данную.

GetFiles () (Только для Directorylnfo)

Возвращает массив объектов Filelnfo, представляющий все файлы, находящиеся в папке.

GetFileSystemlnfos () (Только для Directorylnfo)

Возвращает объекты Filelnfo и Directorylnfo, представляющие все объекты, находящиеся в папке, как массив объектов FileSystemlnfo.

Класс Path представляет ряд статических методов, которые упрощают операции с путевыми именами. Например, предположим, что вы хотите отобразить полное имя файла ReadMe. txt в папке С: Му Documents. Получить путь этого файла можно с помощью следующего кода:

Console.WriteLine (Path.Combine (@" C: My Documents", «ReadMe.txt»));

Данный класс позволяет получить всю необходимую информацию об указанном пути (буква диска, родительский каталог и т. д.) без необходимости строкового анализа пути.

2.2 Компонент FileSystemWatcher

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

Для отслеживания изменений во всех файлах нужно установить свойство Filter равным пустой строке (««) или используйте подстановочные знаки («*.*»). Для отслеживания определенного файла свойство Filter устанавливается равным имени этого файла. Например, для отслеживания изменений в файле MyDoc. txt значения свойства Filter должно быть равным MyDoc.txt. Можно также отслеживать изменения в файлах определенного типа. Например, для отслеживания изменений в текстовых файлах свойство Filter должно быть равным *.txt.

Имеется несколько типов изменений, которые можно отслеживать в каталоге или файле. Например, можно отслеживать изменения атрибутов Attributes, даты и времени последней записи LastWrite или размера Size файлов или каталогов. Это осуществляется путем назначения свойству NotifyFilter одного из выше перечисленных значений.

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

— OnChanged — сообщает об изменениях в атрибутах файла, созданных файлах и удаленных файлах;

— OnRenamed — перечисляет старый и новый пути переименованных файлов и папок, рекурсивно расширяющихся в случае необходимости.

2.3 Компонент BackgroundWorker

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

Чтобы запустить занимающую много времени операцию в фоновом режиме, следует создать экземпляр BackgroundWorker и отслеживать события, сообщающие о ходе выполнения операции и сигнализирующие о ее завершении. Можно создать объект BackgroundWorker программными средствами или перетащить его в форму из вкладки Компоненты Панели элементов. Класс BackgroundWorker, созданный в конструкторе Windows Forms, появляется в области компонентов, а его свойства отображаются в окне «Свойства» .

Чтобы настроить выполнение операции в фоновом режиме, необходимо добавить обработчик события для события DoWork. Вызовите операцию, которая занимает много времени, в этом обработчике событий. Чтобы начать операцию, вызовите RunWorkerAsync. Чтобы получать уведомления о ходе выполнения, необходимо обработать событие ProgressChanged. Если необходимо получать уведомление после завершения операции, обработайте событие RunWorkerCompleted.

2.4 Win API

Несмотря на богатство стандартных библиотек языка c#, иногда все же приходится прибегать к использованию внешних библиотек. В разрабатываемом приложении возникла потребность подписки на системные события подключения/отключения внешнего накопителя данных. Доступ к данным событиям и обеспечивает Win API.

API — это аббревиатура названия Application Programming Interface (интерфейс прикладного программирования). API представляет собой совокупность функций и инструментов, позволяющих программисту создавать приложения (программы), работающие в некоторой среде.

Win API — это набор функций для создания программ, работающих под управлением Microsoft Windows.

Всё взаимодействие пользовательского приложения с операционной системой посредством Win API основано на концепции сообщений. С точки зрения приложения, сообщение является уведомлением о том, что произошло некоторое событие, которое может требовать, а может и не требовать выполнения определенных действий. Это событие может быть следствием действий пользователя, например перемещения курсора или щелчка кнопкой мыши, изменения размеров окна или выбора пункта меню. Кроме того, событие может генерироваться приложением, а также операционной системой. Обычно, эти события принимаются оконной процедурой.

Оконная процедура — это «функция обратного вызова», предназначенная для обработки сообщений, адресованных любому окну того «оконного класса», в котором содержится ссылка на данную процедуру. Эту функцию, обычно, вызывает сама операционная система.

Оконная процедура обычно имеет заголовок со стандартным синтаксисом:

LRESULT CALLBACK Иия_функции (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM IParam)

В этом определении LRESULT — тип возвращаемого значения, hWnd — дескриптор окна, которому адресовано сообщение, uMsg — код сообщения, wParam и IParam — параметры сообщения. Имя функции может быть произвольным, но для главного окна приложения обычно используется имя WndProc.

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

Каждому коду сообщения в Windows сопоставлен уникальный символический идентификатор. Все системные идентификаторы определены при помощи директивы #define в заголовочном файле winuser.h. Это облегчает чтение и понимание программ.

Чаще всего приложение обрабатывает оконные сообщения (window messages), начинающиеся с префикса WM_, в нашем случае это WM_DEVICECHANGE. Данный идентификатор позволяет отсеивать сообщения, связанные с событиями подключаемых usb-устройств. В качестве дополнительных параметров нас интересуют следующие два:

— DBT_DEVICEARRIVAL — сообщает о подключении устройства;

— DBT_DEVICEQUERYREMOVE — сообщает об отключении устройства.

3. Схема диалога пользователя с разработанным приложением

Диалог пользователя с разработанным приложением «DriveSync» начинается с главного окна — рисунок 3. Исходный код данной формы приведен в приложении А.

Рисунок 3 — Главное окно программы

В данном окне, из соответствующей панели пользователю доступны следующие операции над синхронизациями:

— добавить — добавление новой пары синхронизируемых каталогов;

— синхронизировать — синхронизация каталогов (команда доступна в случае необходимости выбора пользователем направления синхронизации);

— изменить — изменение пары синхронизируемых каталогов, а также дополнительных параметров их синхронизации;

— удалить — удаление пары синхронизируемых каталогов;

— включить/отключить — активация/деактивация мониторинга за состоянием каталогов и прерывание текущего процесса синхронизации.

Для сокращения написания введем следующий термин — синхропара, он будет означать сокращенное написание выражения «пара синхронизируемых каталогов».

Для начала нужно создать синхропару. Это можно сделать из соответствующей панели — рисунок 4, появляющейся по нажатию кнопки «Добавить». В поля «Каталог А» и «Каталог Б» нужно ввести полный путь к синхронизируемым каталогам. Сделать это также можно с помощью диалогового окна выбора каталога, появляющегося по нажатию кнопки «Обзор…». Синхронизируемые каталоги должны быть различными и не являться взаимовложенными (во избежание зацикливания синхронизации). Под полями ввода путей есть чекбокс «Автоматическая синхронизация», при установке которого включается мониторинг за изменением содержимого данных каталогов, и файлы и папки данных каталогов синхронизируются автоматически при их изменении в одной из директорий. Если оба введенных пути корректны, то нажатие кнопки «Создать» завершит создание синхропары.

Кнопка «Изменить» становится активна только после выбора одной из пар синхронизации в их списке. Процесс изменения синхропары такой же, как и при создании, только в поля уже введены старые значения.

Рисунок 4 — Панель создания пары синхронизируемых каталогов

Нажатие кнопки «Удалить» или клавиши Delete удалит выбранные в списке записи (если не выбрано ни одной записи — клавиша будет неактивна) и прервет текущий процесс синхронизации всех данных пар каталогов.

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

При синхронизации больших объемов данных в строке состояния можно увидеть название и полосу прогресса копирования текущего файла — рисунок 5.

Рисунок 5 — Окно выбора направления синхронизации содержимого каталогов

При выборе пары каталогов, для которой требуется выбор направления синхронизации (состояние — «Выберите направление») можно вручную указать направление синхронизации. Для этого нужно либо нажать клавишу Enter, либо просто дважды кликнуть по данной записи в списке. После этого появится окно выбора направления синхронизации — рисунок 6.

Рисунок 6 — Окно выбора направления синхронизации содержимого каталогов

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

Рисунок 7 — Контекстное меню выбора направления синхронизации

Исходный код данной формы содержится в приложении Б.

Данное приложение подразумевает постоянную фоновую работу, поэтому логично разместить ее значок в системном трее, откуда, кроме вызова главной формы, можно получить доступ к контекстному меню программы, содержащему два пункта: «Выход» и «Настройки». Кнопка выход, соответственно, завершит работу программы, а вторая кнопка откроет окно настроек — рисунок 8.

Рисунок 8 — Окно настроек

Здесь имеется три пункта:

— «Запрос подтверждения закрытия программы» — данный пункт отвечает за отображения диалогового окна, предлагающего свернуть программу в трей вместо закрытия, которое может отображаться при попытке закрытия главного окна программы;

— «Сворачивать вместо закрытия» — данный пункт настроек отображается только в том случае, если не отмечен верхний;

— «Запускать при старте Windows» — данный пункт отвечает за добавление программы в список автозагрузки системы.

Исходный код данной формы представлен в приложении В.

4. Алгоритм синхронизации каталогов

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

Синхропары могут принимать следующие состояния:

— Analyzing (анализ состояния каталогов);

— Ok (каталоги синхронизированы);

— Off (синхронизация каталогов отключена);

— InProcess (идет процесс синхронизации);

— WaitSolution (ожидание выбора направления синхронизации пользоватлем);

— WaitProcess (в очереди на синхронизацию);

— NotAvailable (один из каталогов недоступен);

— UnSuccess (в процессе синхронизации произошла ошибка).

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

Представленный на предыдущем рисунке алгоритм можно описать следующей последовательностью операций:

1) В начале, синхропара может иметь два состояния: анализ и отключена. Если она отключена, то данная синхропара не принимает участия в дальнейших операциях.

2) Далее, проверяется доступность каталогов синхропары: если один из каталогов не доступен, то ей присваивается состояние «один из каталогов недоступен» и она возвращается в очередь на анализ.

3) Следующим этапом производится проверка флага «авто-синхронизация» (до первого анализа он отключен у всех синхропар) и, если необходима синхронизация, а также отключен данный флаг, то синхропаре присваивается состояние «выбор направления синхронизации».

Рисунок 9 — Схема порядка смены состояний синхропар

4) Далее синхропара помещается в очередь синхронизации. Это происходит сразу после анализа (если присвоено состояние «ожидание обработки»), либо после выбора пользователем направления синхронизации.

5) Затем наступает очередь непосредственной синхронизации. Если в ее процессе происходит некий сбой, то синхронизация прерывается и синхропаре присваивается состояние «ошибка синхронизации», при котором она исключается из цикла синхронизации до тех пор, пока пользователь не перезапустит ее. Если же синхронизация завершена успешно, то синхропаре присваивается состояние «успех» и она возвращается в очередь на анализ.

Этот алгоритм, а также прочее свойства и методы сущности синхропары содержатся в классе SyncDirs. Его исходный код представлен в приложении Г.

Коллекция синхропар и все связанные с ней операции (очереди, групповая обработка и т. д.) реализованы в классе SyncDirsList описанном в приложении Д.

Заключение

В ходе выполнения курсовой работы, было разработано приложение «SyncDirs», предназначенное для автоматизации процесса синхронизации каталогов устройств. Также, в процессе разработки приложения были изучены основы использования методов win API, .net компонентов BackgroundWorker и FileSystemWatcher, а также классов пространства имен System. IO, предназначенных для работы с элементами файловой системы.

Приложение и данный отчет выполнены в полном объеме, намеченном в начале курсового проектирования.

Разработанное приложение, «SyncDirs», предназначено для повседневного использования на компьютерах с любой операционной системой (начиная с Windows XP и новее), на которой установлен .net framework 4.0. Оно будет полезно как для синхронизации документов, так и для ведения медиатеки на устройствах типа «mp3-плеер».

Список использованных источников

1. Нейгел К. C# 2005 и платформа .NET 3.0 для профессионалов. / К. Нейгел, Б. Ивьен, Д. Глинн, М. Скиннер, К. Уотсон. — М.: Вильямс, 2008. — 1376 с.

2. Троелсен, Э. Язык программирования С# 5.0 и платформа .NET 4.5, 6-е изд. / Э. Троелсен. — М.: Вильямс, 2013. — 1321 с.

3. Фленов, М. Библия С# - 2-е изд., перераб. и доп. / М. Фленов. — СПб.: БХВ-Петербург, 2011. — 560 с.

4. Щупак, Ю. Win32 API. Эффективная разработка приложений / Ю. Щупак. — СПб.: Питер, 2007. — 572 с.

5. MSDN: Руководство по программированию на C#

Приложение А

Программный код главной формы

public partial class MainForm: Form

{

public const string FileRemConf = «SyncDriveConfig.ini» ;

[DllImport («user32.dll»)]

static extern int ShowWindow (IntPtr hWnd, uint Msg);

const uint SW_RESTORE = 0×09;

DriveDetector driveDetector = null;

SyncDirsList syncDirs;

List idRemovableDrives;

List nameRemovableDrives;

Form syncForm = null;

ListViewColumnSorter lvwColumnSorter;

bool startMinimized;

public MainForm (bool startMinimized)

{

InitializeComponent ();

syncDirs = new SyncDirsList (lVDirs, statusStrip1);

SettingsForm.MainForm = this;

SettingsForm.Pointer.LoadSettings ();

idRemovableDrives = SettingsForm.Pointer.IdRemovableDrives;

nameRemovableDrives = SettingsForm. Pointer

.NameRemovableDrives;

this.startMinimized = startMinimized;

if (startMinimized)

{

ShowInTaskbar = false;

Opacity = 0;

}

for (int i = 0; i < lVDirs.Columns.Count && i < SettingsForm

.Pointer.ColSizes.Count; i++)

lVDirs.Columns[i]. Width = (int)SettingsForm.Pointer

.ColSizes[i];

StringCollection pathsA = SettingsForm.Pointer.PathsA;

StringCollection pathsB = SettingsForm.Pointer.PathsB;

StringCollection marksA = SettingsForm.Pointer.MarksA;

StringCollection marksB = SettingsForm.Pointer.MarksB;

List states = SettingsForm.Pointer.States;

List allowAutoSyncs = SettingsForm. Pointer

.AllowAutoSyncs;

for (int i = 0; i < pathsA. Count; i++)

syncDirs.Add (new SyncDirs (pathsA[i], pathsB[i], marksA[i], marksB[i], states[i], allowAutoSyncs[i]));

foreach (DriveInfo drive in DriveInfo. GetDrives ())

markRemoveble (drive);

timerInterfaceUpdate.Enabled = true;

lvwColumnSorter = new ListViewColumnSorter ();

lVDirs.ListViewItemSorter = lvwColumnSorter;

}

private void Form1_Shown (object sender, EventArgs e)

{

driveDetector = new DriveDetector ();

driveDetector.DeviceArrived +=newDriveDetectorEventHandler (OnDriveArrived);

if (startMinimized)

{

Hide ();

Opacity = 100;

ShowInTaskbar = true;

}

}

void OnDriveArrived (object sender, DriveDetectorEventArgs e)

{

foreach (DriveInfo drive in DriveInfo. GetDrives ())

markRemoveble (drive);

}

void MainForm_FormClosing (object sender, FormClosingEventArgs e)

{

if (SettingsForm.Pointer.AlowQuestionForClose &&

Opacity > 0)

{

bool alow = false;

MsgBoxCheck.MessageBox dlg = new MsgBoxCheck

.MessageBox ();

DialogResult dr = dlg. Show (

" Запомнить выбор (можно изменить в настройках)" ,

" Свернуть в трей вместо закрытия?" ,

" Закрытие окна программы…" + new String (' ', 25),

MessageBoxButtons.YesNo, MessageBoxIcon. Question, ref alow);

SettingsForm.Pointer.AlowQuestionForClose = !alow;

SettingsForm.Pointer.AlowClose = dr == DialogResult. No;

}

if (!SettingsForm.Pointer.AlowClose)

{

Hide ();

e.Cancel = true;

}

else

{

SettingsForm.Pointer.ColSizes.Clear ();

for (int i = 0; i < lVDirs.Columns.Count; i++)

SettingsForm.Pointer.ColSizes

.Add (lVDirs.Columns[i]. Width);

StringCollection pathsA = new StringCollection ();

StringCollection pathsB = new StringCollection ();

StringCollection marksA = new StringCollection ();

StringCollection marksB = new StringCollection ();

List states = new List ();

List allowAutoSync = new List ();

foreach (SyncDirs item in syncDirs)

{

pathsA.Add (item.PathA);

pathsB.Add (item.PathB);

marksA.Add (item.MarkA);

marksB.Add (item.MarkB);

states.Add (item.State);

allowAutoSync.Add (item.AllowAutoSync);

}

SettingsForm.Pointer.PathsA = pathsA;

SettingsForm.Pointer.PathsB = pathsB;

SettingsForm.Pointer.MarksA = marksA;

SettingsForm.Pointer.MarksB = marksB;

SettingsForm.Pointer.States = states;

SettingsForm.Pointer.AllowAutoSyncs = allowAutoSync;

SettingsForm.Pointer.IdRemovableDrives = idRemovableDrives;

SettingsForm.Pointer.NameRemovableDrives = nameRemovableDrives;

if (syncForm ≠ null)

syncForm.Close ();

SettingsForm.Pointer.SaveSettings ();

if (syncDirs.CurrentTask.State == SyncState. InProcess)

syncDirs.CurrentTask.Cancel ();

SettingsForm.Pointer.Close ();

}

}

private void tSMIExit_Click (object sender, EventArgs e)

{

Opacity = 0;

SettingsForm.Pointer.AlowClose = true;

Close ();

}

private void btCancelAdd_Click (object sender, EventArgs e)

{

tBPathA.Text = «» ;

tBPathB.Text = «» ;

gBAddSync.Visible = false;

gBCommandsSync.Visible = true;

lVDirs.Enabled = true;

lVDirs.Focus ();

}

private void btAddSync_Click (object sender, EventArgs e)

{

btApplyAdd.Visible = true;

btApplyEdit.Visible = false;

gBAddSync.Visible = true;

gBCommandsSync.Visible = false;

lVDirs.Enabled = false;

cBAutoSync.Checked = true;

}

private void btReviewA_Click (object sender, EventArgs e)

{

folderBrowserDialog.SelectedPath = tBPathA. Text;

DialogResult result = folderBrowserDialog. ShowDialog ();

if (result == DialogResult. OK)

tBPathA.Text = folderBrowserDialog. SelectedPath;

}

private void btReviewB_Click (object sender, EventArgs e)

{

folderBrowserDialog.SelectedPath = tBPathB. Text;

DialogResult result = folderBrowserDialog. ShowDialog ();

if (result == DialogResult. OK)

tBPathB.Text = folderBrowserDialog. SelectedPath;

}

private void btApplyAdd_Click (object sender, EventArgs e)

{

string errMess = null;

SyncDirs syncDirs = new SyncDirs (tBPathA.Text, tBPathB. Text, SyncState. Analyzing, cBAutoSync. Checked);

if (SyncDirs.IsValidPaths (tBPathA.Text, tBPathB. Text,

ref errMess))

{

foreach (SyncDirs item in this. syncDirs)

{

if (item.PathA == tBPathA. Text &&

item.PathB == tBPathB. Text || item. PathB == tBPathA. Text &&

item.PathA == tBPathB. Text)

{

errMess =

" Данная пара каталогов уже есть в списке синхронизации!" ;

break;

}

}

if (errMess == null && wellBeCycle (tBPathA.Text, tBPathB. Text))

errMess =

" Синхронизация данных каталогов приведет к зацикливанию!" ;

if (errMess == null)

this.syncDirs.Add (syncDirs);

}

if (errMess ≠ null)

{

tSSLabel.Text = errMess;

tSSIcon.Visible = true;

}

else

{

DriveInfo driveA = new DriveInfo (tBPathA.Text[0]. ToString ());

DriveInfo driveB = new DriveInfo (tBPathB.Text[0]. ToString ());

markRemoveble (driveA, true);

markRemoveble (driveB, true);

btCancelAdd_Click (sender, e);

}

lVDirs.Focus ();

}

private void btApplyEdit_Click (object sender, EventArgs e)

{

string errMess = null;

SyncDirs curItem = (SyncDirs)lVDirs.SelectedItems[0]. Tag;

if (!((curItem.PathA == tBPathA. Text && curItem. PathB == tBPathB. Text) || (curItem.PathB == tBPathA. Text && curItem. PathA == tBPathB. Text)) && SyncDirs. IsValidPaths (tBPathA.Text, tBPathB. Text, ref errMess))

{

foreach (SyncDirs item in syncDirs)

{

if (item ≠ curItem && (item.PathA == tBPathA. Text && item. PathB == tBPathB. Text || item. PathB == tBPathA. Text && item. PathA == tBPathB. Text))

{

errMess = «Данная пара каталогов уже есть в списке синхронизации!» ;

break;

}

}

if (wellBeCycle (tBPathA.Text, tBPathB. Text))

errMess = «Синхронизация данных каталогов приведет к зацикливанию!» ;

}

if (errMess ≠ null)

{

tSSLabel.Text = errMess;

tSSIcon.Visible = true;

syncDirs.StateChanged = true;

}

else

{

curItem.AllowAutoSync = cBAutoSync. Checked;

curItem.PathA = tBPathA. Text;

curItem.PathB = tBPathB. Text;

DriveInfo driveA = new DriveInfo (tBPathA.Text[0]. ToString ());

DriveInfo driveB = new DriveInfo (tBPathB.Text[0]. ToString ());

markRemoveble (driveA, true);

markRemoveble (driveB, true);

btCancelAdd_Click (sender, e);

}

lVDirs.Focus ();

}

bool isSubDir (string dirA, string dirB)

{

if (dirA == dirB)

return true;

else if (dirA.Length > dirB. Length)

{

if (dirA.StartsWith (dirB) && dirA. Substring (dirB.Length).Contains («»))

return true;

}

else

{

if (dirB.StartsWith (dirA) && dirB. Substring (dirA.Length).Contains («»))

return true;

}

return false;

}

bool wellBeCycle (string pathA, string pathB,

ist tackedItems = null)

{

if (tackedItems == null)

tackedItems = new List ();

foreach (SyncDirs item in syncDirs)

(isSubDir (pathA, item. PathB) && isSubDir (pathB, item. PathA)))

return true;

if (isSubDir (pathA, item. PathA) && wellBeCycle (item.PathB, pathB, new List (tackedItems)))

return true;

if (isSubDir (pathA, item. PathB) && wellBeCycle (item.PathA, pathB, new List (tackedItems)))

return true;

tackedItems.Remove (item);

return false;

}

void markRemoveble (DriveInfo drive, bool append = false)

{

StringBuilder resultFile = new StringBuilder ();

String newDrive = drive.RootDirectory.ToString ();

bool found = false;

int id = 1, j = 0;

if (drive.DriveType == DriveType. Removable && drive. IsReady)

{

try

{

using (StreamReader sr = new StreamReader (newDrive + FileRemConf))

{

String line;

String bufPathFile;

int bufId;

while ((line = sr. ReadLine ()) ≠ null)

{

if (line.StartsWith («syncronization»))

{

bufId = int. Parse (line.Substring (15));

bufPathFile = sr. ReadLine ();

resultFile.AppendLine (line);

for (int i = 0; i < idRemovableDrives. Count; i++)

{

if (idRemovableDrives[i] == id && nameRemovableDrives[i] == bufPathFile)

{

resultFile.AppendLine (newDrive);

id = bufId;

found = true;

j = i;

}

}

if (!found)

{

resultFile.AppendLine (bufPathFile);

id = bufId + 1;

}

}}

sr.Close ();}}

catch (Exception)

{ }

if (found)

{syncDirs.UpdatePaths (nameRemovableDrives[j], newDrive);

nameRemovableDrives[j] = newDrive;}

else

{idRemovableDrives.Add (id);

nameRemovableDrives.Add (newDrive);

}

if (File.Exists (newDrive + FileRemConf))

File.SetAttributes (newDrive + FileRemConf, FileAttributes. Normal);

if (File.Exists (newDrive + FileRemConf) || append)

{if (!found && append)

using (StreamWriter w = File. AppendText (drive.RootDirectory + FileRemConf))

{

w.WriteLine («syncronization «+ id);

w.WriteLine (newDrive);

w.Close ();}

else

using (StreamWriter w = new StreamWriter (drive.RootDirectory.ToString () + FileRemConf))

{

w.Write (resultFile);

w.Close ();

};

File.SetAttributes (newDrive + FileRemConf, FileAttributes. Hidden);

}

}

}

private void tBPath_TextChanged (object sender, EventArgs e)

{

tSSLabel.Text = «» ;

tSSIcon.Visible = false;

}

private void btDelSync_Click (object sender, EventArgs e)

{

foreach (ListViewItem item in lVDirs. SelectedItems)

{

syncDirs.Remove ((SyncDirs)item.Tag);

}

btSync.Enabled = false;

btEditSync.Enabled = false;

btDelSync.Enabled = false;

btOnSync.Enabled = false;

btOffSync.Enabled = false;

}

private void btOnSync_Click (object sender, EventArgs e)

{

foreach (DriveInfo drive in DriveInfo. GetDrives ())

markRemoveble (drive);

syncDirs.SelSetState (SyncState.Analyzing);

}

private void btOffSync_Click (object sender, EventArgs e)

{

syncDirs.SelSetState (SyncState.Off);

}

private void lVDirs_ItemSelectionChanged (object sender, ListViewItemSelectionChangedEventArgs e)

{

bool isSelected = lVDirs.SelectedItems.Count > 0;

bool isNeedSync = lVDirs.SelectedItems.Count > 0;

foreach (ListViewItem item in lVDirs. SelectedItems)

if (!((item.Tag as SyncDirs).State == SyncState. WaitSolution))

{

isNeedSync = false;

break;

}

btSync.Enabled = isNeedSync;

btEditSync.Enabled = lVDirs.SelectedItems.Count == 1;

btDelSync.Enabled = isSelected;

btOnSync.Enabled = isSelected;

btOffSync.Enabled = isSelected;

((SyncDirs)e.Item.Tag).Selected = e. IsSelected;

}

private void btEditSync_Click (object sender, EventArgs e)

{

btApplyAdd.Visible = false;

btApplyEdit.Visible = true;

tBPathA.Text = (lVDirs.SelectedItems[0]. Tag as SyncDirs).PathA;

tBPathB.Text = (lVDirs.SelectedItems[0]. Tag as SyncDirs).PathB;

cBAutoSync.Checked = (lVDirs.SelectedItems[0]. Tag as SyncDirs).AllowAutoSync;

gBAddSync.Visible = true;

//statusStrip1.Visible = true;

gBCommandsSync.Visible = false;

lVDirs.Enabled = false;

}

void notifyIcon_MouseClick (object sender, MouseEventArgs e)

{

if (e.Button == System.Windows.Forms.MouseButtons.Left)

{

if (WindowState == FormWindowState. Minimized || !Visible)

{

Show ();

Activate ();

ShowWindow (this.Handle, SW_RESTORE);

}

else

Hide ();

}

else

{

List items = new List ();

foreach (DriveInfo drive in DriveInfo. GetDrives ())

if (drive.DriveType == DriveType. Removable && drive. IsReady)

{

ToolStripItem item = new System.Windows.Forms.ToolStripMenuItem («Извлечь «+ drive. VolumeLabel + «(«+ drive. RootDirectory + «)»);

item.Tag = drive.RootDirectory.ToString ()[0];

item.Click += new EventHandler (Eject_Click);

items.Add (item);

}

if (items.Count > 0)

items.Add (new ToolStripSeparator ());

foreach (ToolStripItem item in cMSTray. Items)

if (item.Tag ≠ null && item.Tag.ToString () == «1»)

items.Add (item);

cMSTray.Items.Clear ();

cMSTray.Items.AddRange (items.ToArray ());

MethodInfo mi = typeof (NotifyIcon).GetMethod («ShowContextMenu», BindingFlags. Instance | BindingFlags. NonPublic);

mi.Invoke (notifyIcon, null);

}

}

private void Eject_Click (object sender, EventArgs e)

{

List syncs = new List ();

foreach (SyncDirs item in syncDirs)

{

if (item.State ≠ SyncState. Off && (

item.PathA.StartsWith ((sender as ToolStripItem).Tag.ToString ()) ||

item.PathB.StartsWith ((sender as ToolStripItem).Tag.ToString ())))

{

item.State = SyncState. Off;

syncs.Add (item);

}

}

DriveDetector.EjectDrive ((char)(sender as ToolStripItem).Tag);

foreach (SyncDirs item in syncs)

item.State = SyncState. Analyzing;

}

private void timerScanner_Tick (object sender, EventArgs e)

{

notifyIcon.Text = syncDirs. SyncsUpdate ();

}

private void lVDirs_DoubleClick (object sender, EventArgs e)

{

if (lVDirs.SelectedItems.Count == 1 && lVDirs. SelectedItems[0]. SubItems[2].Text == StateStrings. Items[(int)SyncState.WaitSolution])

{

syncForm = new SynchronizationForm ((SyncDirs)lVDirs.SelectedItems[0]. Tag);

syncForm.ShowDialog ();

syncForm = null;

}

}

void lVDirs_ColumnClick (object sender, ColumnClickEventArgs e)

{

if (e.Column == lvwColumnSorter. SortColumn)

{

if (lvwColumnSorter.Order == SortOrder. Ascending)

lvwColumnSorter.Order = SortOrder. Descending;

else

lvwColumnSorter.Order = SortOrder. Ascending;

}

else

{

lvwColumnSorter.SortColumn = e. Column;

lvwColumnSorter.Order = SortOrder. Ascending;

}

this.lVDirs.Sort ();

}

private void btSync_Click (object sender, EventArgs e)

{

SyncDirs currSync;

foreach (ListViewItem item in lVDirs. SelectedItems)

{

currSync = (item.Tag as SyncDirs);

currSync.AutoSync = true;

currSync.State = SyncState. Analyzing;

}

}

private void lVDirs_KeyUp (object sender, KeyEventArgs e)

{

switch (e.KeyValue)

{ case (int)Keys.Delete:

if (btDelSync.Enabled)

btDelSync_Click (null, null);

break;

case (int)Keys.Insert:

btAddSync_Click (null, null);

break;

case (int)Keys.F2:

btEditSync_Click (null, null);

break;

case (int)Keys.Enter:

if (e.Shift && btSync. Enabled)

btSync_Click (null, null);

else

lVDirs_DoubleClick (null, null);

break;

case (int)Keys.Escape:

btOffSync_Click (null, null);

break;

case (int)Keys.F1:

btOnSync_Click (null, null);

break;}}

private void tBPathA_KeyUp (object sender, KeyEventArgs e)

{

switch (e.KeyData)

{

case Keys. Escape:

btCancelAdd_Click (null, null);

break;

case Keys. Enter:

if (btApplyAdd.Visible)

btApplyAdd_Click (null, null);

if (btApplyEdit.Visible)

btApplyEdit_Click (null, null);

break;

}}}

синхронизация файл программа

Приложение Б

Программный код формы «Выбор направления синхронизации»

public partial class SynchronizationForm: Form

{

SyncDirs syncDirs;

ListViewColumnSorter lvwColumnSorter;

public SynchronizationForm ()

{

InitializeComponent ();

Size = SettingsForm.SyncForm.Size;

Location = SettingsForm.SyncForm.Location;

WindowState = SettingsForm.SyncForm.WindowState;

for (int i = 0; i < lVFiles.Columns.Count && i < SettingsForm.Pointer.ColSizesSyncForm.Count; i++)

lVFiles.Columns[i]. Width = (int)SettingsForm.Pointer.ColSizesSyncForm[i];

}

public SynchronizationForm (SyncDirs syncDirs)

: this ()

{

this.syncDirs = syncDirs;

}

private void SynchronizationForm_Shown (object sender, EventArgs e)

{

tBPathA.Text = (syncDirs.MarkA == «»? «»: «[» + syncDirs. MarkA + «] «) + syncDirs. PathA;

tBPathB.Text = (syncDirs.MarkB == «»? «»: «[» + syncDirs. MarkB + «] «) + syncDirs. PathB;

cBSyncMode.SelectedIndex = -1;

lvwColumnSorter = new ListViewColumnSorter ();

lVFiles.ListViewItemSorter = lvwColumnSorter;

foreach (var item in syncDirs. SyncList)

this.lVFiles.Sort ();

}

void SynchronizationForm_FormClosing (obj s, FormClosingEventArgs e)

{

SettingsForm.SyncForm.WindowState = WindowState;

Opacity = 0;

WindowState = FormWindowState. Normal;

SettingsForm.SyncForm.Size = Size;

SettingsForm.SyncForm.Location = Location;

SettingsForm.Pointer.ColSizesSyncForm.Clear ();

for (int i = 0; i < lVFiles.Columns.Count; i++)

SettingsForm.Pointer.ColSizesSyncForm.Add (lVFiles.Columns[i]. Width);

foreach (ListViewItem item in lVFiles. Items)

{

syncDirs.SyncList[" «+ item. Text] = (SyncMode)item.Tag;

}

if (syncDirs.AutoSync)

syncDirs.State = SyncState. Analyzing;

}

private void cmSymcMode_Opening (object sender, CancelEventArgs e)

{

if (lVFiles.SelectedItems.Count == 0)

e.Cancel = true;

}

void cBSyncMode_SelectedIndexChanged (object sender, EventArgs e)

{

if (cBSyncMode.SelectedIndex == -1)

return;

if (lVFiles.SelectedItems.Count == 0 || lVFiles.SelectedItems.Count == lVFiles.Items.Count)

foreach (ListViewItem item in lVFiles. Items)

{

item.SubItems[3]. Text = ModeStrings. Items[cBSyncMode.SelectedIndex];

item.Tag = (SyncMode)cBSyncMode.SelectedIndex;

}

else

{

foreach (ListViewItem item in lVFiles. SelectedItems)

{

item.SubItems[3]. Text = ModeStrings. Items[cBSyncMode.SelectedIndex];

item.Tag = (SyncMode)cBSyncMode.SelectedIndex;

}

}

}

private void tSMNoneSync_Click (object sender, EventArgs e)

{

cBSyncMode.SelectedIndex = (int)SyncMode.NoneNync;

}

void lVFiles_SelectedIndexChanged (object sender, EventArgs e)

{

SyncMode prewMode;

if (lVFiles.SelectedItems.Count == 0)

{

prewMode = (SyncMode)lVFiles.Items[0]. Tag;

foreach (ListViewItem item in lVFiles. Items)

{

if ((SyncMode)item.Tag ≠ prewMode)

{

cBSyncMode.SelectedIndex = -1;

return;

}

}

}

else

{

prewMode = (SyncMode)lVFiles.SelectedItems[0]. Tag;

foreach (ListViewItem item in lVFiles. SelectedItems)

{

if ((SyncMode)item.Tag ≠ prewMode)

{

cBSyncMode.SelectedIndex = -1;

return;

}

}

}

cBSyncMode.SelectedIndex = (int)prewMode;

}

void вНаправленииАBToolStripMenuItem_Click (object s, EventArgs e)

{

cBSyncMode.SelectedIndex = (int)SyncMode.AToB;

}

void вНаправленииВAToolStripMenuItem_Click (object s, EventArgs e)

{

cBSyncMode.SelectedIndex = (int)SyncMode.BToA;

}

void lVFiles_ColumnClick (object sender, ColumnClickEventArgs e)

{

if (e.Column == lvwColumnSorter. SortColumn)

{

if (lvwColumnSorter.Order == SortOrder. Ascending)

lvwColumnSorter.Order = SortOrder. Descending;

else

lvwColumnSorter.Order = SortOrder. Ascending;

}

else

{

lvwColumnSorter.SortColumn = e. Column;

lvwColumnSorter.Order = SortOrder. Ascending;

}

this.lVFiles.Sort ();

}

private void btSync_Click (object sender, EventArgs e)

{

syncDirs.AutoSync = true;

Close ();

}

}

Приложение В

Процедуры сохранения / восстановления настроек программы

public void LoadSettings ()

{

Settings set = Settings. Default;

ArrayList states;

ArrayList iDRemovableDrives;

ArrayList allowAutoSyncs;

StringCollection nameRemovableDrives;

RegistryKey rkApp = Registry.CurrentUser.OpenSubKey (

" SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);

cBAutoRun.Checked = (rkApp.GetValue («DriveSync») ≠ null);

alowClose = set. AlowClose;

alowQuestionForClose = set. AlowQuestionForClose;

cBAlowMinimize.Checked = !alowClose;

cBShowQuestMinimize.Checked = alowQuestionForClose;

colSizes = set. CollsSize;

colSizesSyncForm = set. CollsSizeSyncForm;

pathsA = set. PathsA;

pathsB = set. PathsB;

marksA = set. MarksA;

marksB = set. MarksB;

states = set. States;

MainForm.Size = set. Size;

MainForm.Location = set. Location;

SyncForm.Size = set. SizeSyncForm;

SyncForm.Location = set. LocationSyncForm;

States = new List ();

if (states ≠ null)

{foreach (SyncState item in states)

{if (item ==SyncState.Off)

States.Add (item);

else

States.Add (SyncState.Analyzing);}}

AllowAutoSyncs = new List ();

allowAutoSyncs = set. AllowAutoSyncs;

if (allowAutoSyncs ≠ null)

{

foreach (object item in allowAutoSyncs)

AllowAutoSyncs.Add ((bool)item);

}

iDRemovableDrives = set. IDRemovableDrives;

nameRemovableDrives = set. RemovableDrives;

this.idRemovableDrives = new List ();

this.nameRemovableDrives = new List ();

if (iDRemovableDrives ≠ null)

{

for (int i = 0; i < iDRemovableDrives. Count; i++)

{

this.idRemovableDrives.Add ((int)iDRemovableDrives[i]);

this.nameRemovableDrives.Add (nameRemovableDrives[i]);

}

}

if (set.SyncFormMax)

SyncForm.WindowState = FormWindowState. Maximized;

if (set.FormMax)

MainForm.WindowState = FormWindowState. Maximized;

if (colSizes == null)

colSizes = new ArrayList ();

if (colSizesSyncForm == null)

colSizesSyncForm = new ArrayList ();

if (pathsB == null)

pathsB = new StringCollection ();

if (pathsA == null)

pathsA = new StringCollection ();

if (marksB == null)

marksB = new StringCollection ();

if (marksA == null)

marksA = new StringCollection ();}

public void SaveSettings (bool saveWinSize = true)

{Settings set = Settings. Default;

ArrayList states = new ArrayList ();

ArrayList iDRemovableDrives = new ArrayList ();

ArrayList allowAutoSyncs = new ArrayList ();

StringCollection namesRemovableDrives = new StringCollection ();

RegistryKey rkApp = Registry.CurrentUser.OpenSubKey (

" SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);

if (cBAutoRun.Checked)

rkApp.SetValue («DriveSync», Application.ExecutablePath.ToString () + «/StartMinimized»);

else

rkApp.DeleteValue («DriveSync», false);

foreach (SyncState item in this. states)

{

if (item ≠ SyncState. Off)

states.Add (SyncState.Analyzing);

else

states.Add (item);

}

foreach (int item in idRemovableDrives)

iDRemovableDrives.Add (item);

foreach (string item in nameRemovableDrives)

namesRemovableDrives.Add (item);

foreach (bool item in AllowAutoSyncs)

allowAutoSyncs.Add (item);

set.AllowAutoSyncs = allowAutoSyncs;

set.AlowClose = alowClose;

set.AlowQuestionForClose = alowQuestionForClose;

set.PathsA = pathsA;

set.PathsB = pathsB;

set.MarksA = marksA;

set.MarksB = marksB;

set.States = states;

if (saveWinSize)

{

set.FormMax = (MainForm.WindowState == FormWindowState. Maximized);

MainForm.Opacity = 0;

MainForm.WindowState = FormWindowState. Normal;

set.Size = MainForm. Size;

set.SyncFormMax = (SyncForm.WindowState == FormWindowState. Maximized);

SyncForm.WindowState = FormWindowState. Normal;

set.SizeSyncForm = SyncForm. Size;

}

set.Location = MainForm. Location;

set.LocationSyncForm = SyncForm. Location;

set.CollsSize = colSizes;

set.CollsSizeSyncForm = colSizesSyncForm;

set.IDRemovableDrives = iDRemovableDrives;

set.RemovableDrives = namesRemovableDrives;

set.Save ();}

Приложение Г

Исходный код сущности синхропары

public enum SyncState

{

Analyzing = 0,

Ok = 1,

Off = 2,

InProcess = 3,

WaitSolution = 4,

WaitProcess = 5,

NotAvailable = 6,

UnSuccess = 7

}

public enum SyncMode

{

NoneNync,

AToB,

BToA

}

static class StateStrings

{

public static readonly string[] Items =

{

" Анализируется…" ,

" Готово" ,

" Отключено" ,

" Синхронизируется…" ,

" Выберите направление" ,

" В очереди на синхронизацию" ,

" Один из каталогов недоступен" ,

" Не все файлы синхронизировались"

};

}

static class ModeStrings

{

public static readonly string[] Items =

{

" Не синхронизировать" ,

" А -> Б" ,

" Б -> А"

};

}

public class SyncDirs

{

bool remA, remB;

string pathA;

string pathB;

string markA;

string markB;

bool selected;

bool needShow;

bool needSync;

bool autoSync;

bool allowAutoSync;

bool success;

SyncState state;

FileSystemWatcher fileWatcherA;

FileSystemWatcher fileWatcherB;

Dictionary syncList;

string progressMess;

double progressPercent;

private AutoResetEvent _resetEvent = new AutoResetEvent (false);

private BackgroundWorker backgroundSync;

public double ProgressPercent

{

get { return progressPercent; }

}

public string ProgressMess

{

get { return progressMess; }

}

internal Dictionary SyncList

{

get { return syncList; }

}

public bool AllowAutoSync

{

get { return allowAutoSync; }

set

{

if (value && value ≠ allowAutoSync && state == SyncState. Off)

state = SyncState. Analyzing;

allowAutoSync = value;

}

}

public bool AutoSync

{

get { return autoSync; }

set { autoSync = value; }

}

public string MarkA

{

get { return markA; }

}

public string MarkB

{

get { return markB; }

}

public bool NeedSync

{

get { return needSync; }

}

public string PathA

{

get { return pathA; }

set

{

if (pathA ≠ value)

{

if (fileWatcherA ≠ null)

fileWatcherA.Dispose ();

fileWatcherA = null;

}

needSync = true;

pathA = value;

if (pathA.Length > 0 && ((pathA[0] <= 'z' && pathA[0] >= 'a') || (pathA[0] <= 'Z' && pathA[0] >= 'A')))

{

DriveInfo drive = new DriveInfo (pathA.Substring (0, 1));

if (drive.IsReady)

markA = drive. VolumeLabel;

}

}

}

public string PathB

{

get { return pathB; }

set

{

if (pathB ≠ value)

{

if (fileWatcherB ≠ null)

fileWatcherB.Dispose ();

fileWatcherB = null;

}

needSync = true;

pathB = value;

if (pathB.Length > 0 && ((pathB[0] <= 'z' && pathB[0] >= 'a') || (pathB[0] <= 'Z' && pathB[0] >= 'A')))

{

DriveInfo drive = new DriveInfo (pathB.Substring (0, 1));

if (drive.IsReady)

markB = drive. VolumeLabel;

}

}

}

public SyncState State

{

get

{

return state;

}

set

{

state = value;

switch (state)

{

case SyncState. Analyzing:

needSync = true;

break;

case SyncState. Off:

case SyncState. NotAvailable:

if (fileWatcherA ≠ null)

fileWatcherA.Dispose ();

if (fileWatcherB ≠ null)

fileWatcherB.Dispose ();

fileWatcherA = null;

fileWatcherB = null;

autoSync = false;

syncList.Clear ();

break;

case SyncState. Ok:

case SyncState. UnSuccess:

syncList.Clear ();

autoSync = true;

if (fileWatcherA == null && AllowAutoSync)

NotifyFilters.FileName

if (fileWatcherB == null && AllowAutoSync)

NotifyFilters.LastWrite;

fileWatcherB.IncludeSubdirectories = true;

fileWatcherB.EnableRaisingEvents = true;

break;

}

}

}

public bool Selected

{

get { return selected; }

set { selected = value; }

}

public string StateString

{

get

{

if (state == SyncState. InProcess && progressMess == «»)

return StateStrings. Items[(int)SyncState.Analyzing];

else

return StateStrings. Items[(int)state];

}

}

public bool NeedShow

{

get

{

bool buf = needShow;

needShow = false;

return buf;

}

set { needShow = value; }

}

void dirChanged (object sender, FileSystemEventArgs e)

{

if (e.FullPath.StartsWith (pathA) && !e.FullPath.StartsWith (pathB))

remA = true;

else

remB = true;

state = SyncState. Analyzing;

needSync = true;

if (fileWatcherA ≠ null)

{

fileWatcherA.Dispose ();

fileWatcherA = null;

}

if (fileWatcherB ≠ null)

{

fileWatcherB.Dispose ();

fileWatcherB = null;

}

}

void dirDeleted (object sender, FileSystemEventArgs e)

{

if (e.FullPath.StartsWith (pathA) && !e.FullPath.StartsWith (pathB))

remA = true;

else

remB = true;

state = SyncState. Analyzing;

needSync = true;

if (fileWatcherA ≠ null)

{

fileWatcherA.Dispose ();

fileWatcherA = null;

}

if (fileWatcherB ≠ null)

{

fileWatcherB.Dispose ();

fileWatcherB = null;

}

}

void dirRenamed (object sender, RenamedEventArgs e)

{

if (e.OldFullPath.StartsWith (pathA) && !e.OldFullPath.StartsWith (pathB))

remA = true;

else

remB = true;

state = SyncState. Analyzing;

needSync = true;

if (fileWatcherA ≠ null)

{

fileWatcherA.Dispose ();

fileWatcherA = null;

}

if (fileWatcherB ≠ null)

{

fileWatcherB.Dispose ();

fileWatcherB = null;

}

}

public bool DirsExists ()

{

if (Directory.Exists (pathA) && Directory. Exists (pathB))

return true;

else

return false;

}

public SyncDirs ()

{

progressMess = «» ;

progressPercent = -1;

state = SyncState. Ok;

remA = false;

remB = false;

selected = false;

needShow = false;

needSync = true;

autoSync = false;

allowAutoSync = true;

syncList = new Dictionary ();

backgroundSync = new BackgroundWorker ();

backgroundSync.WorkerSupportsCancellation = true;

backgroundSync.DoWork += new System.ComponentModel.DoWorkEventHandler (this.backgroundSync_DoWork);

backgroundSync.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler (this.backgroundSync_RunWorkerCompleted);

backgroundSync.ProgressChanged += new ProgressChangedEventHandler (reportProgress);

backgroundSync.WorkerReportsProgress = true;

}

public SyncDirs (string pathA, string pathB, SyncState state, bool allowAutoSync)

: this ()

{

PathA = pathA;

PathB = pathB;

if (!allowAutoSync && state == SyncState. Off)

state = SyncState. Analyzing;

this.state = state;

this.allowAutoSync = allowAutoSync;

}

public SyncDirs (string pathA, string pathB, string markA, string markB, SyncState state, bool allowAutoSync)

: this (pathA, pathB, state, allowAutoSync)

{

this.markA = markA;

this.markB = markB;

}

public static bool IsValidPaths (string pathA, string pathB,

ref string errMess)

{

errMess = null;

if (pathA == «»)

errMess = «Выберите каталог А» ;

else if (pathB == «»)

errMess = «Выберите каталог Б» ;

else

{

if (pathA[pathA.Length — 1] == '\')

pathA = pathA. Remove (pathA.Length — 1, 1);

if (pathB[pathB.Length — 1] == '\')

pathB = pathB. Remove (pathB.Length — 1, 1);

if (!Directory.Exists (pathA))

errMess =

" Такого пути не существует! (Каталог А)" ;

else if (!Directory.Exists (pathB))

errMess =

" Такого пути не существует! (Каталог Б)" ;

else if (Path.Equals (pathA, pathB))

errMess =

" Нет смысла синхронизировать каталог сам с собой…" ;

else if (pathA.StartsWith (pathB) && pathA. Substring (pathB.Length).Contains («»))

errMess =

" Не надо пытаться синхронизировать корневой каталог с его дочерними…" ;

else if (pathB.StartsWith (pathA) && pathB. Substring (pathA.Length).Contains («»))

errMess =

" Не надо пытаться синхронизировать корневой каталог с его дочерними…" ;

}

return (errMess == null);

}

void backgroundSync_RunWorkerCompleted (object s, RunWorkerCompletedEventArgs e)

{

needShow = true;

needSync = false;

progressMess = «» ;

progressPercent = -1;

try

{

if (!success && File. Exists (CustomFileCopier.lastCopiedFile))

File.Delete (CustomFileCopier.lastCopiedFile);

}

catch (Exception)

{

}

_resetEvent.Set ();

}

public void Cancel ()

{

success = false;

_resetEvent.WaitOne ();

try

{

if (File.Exists (CustomFileCopier.lastCopiedFile))

File.Delete (CustomFileCopier.lastCopiedFile);

}

catch (Exception)

{ }

}

public void Analyze ()

{

State = SyncState. InProcess;

if (!backgroundSync.IsBusy)

backgroundSync.RunWorkerAsync ();

}

private void backgroundSync_DoWork (object sender, System.ComponentModel.DoWorkEventArgs e)

{

string fileSystemEntry;

FileInfo fileA;

FileInfo fileB;

SyncMode mode;

if (syncList.Count == 0)

{

foreach (string currentFile in Directory. EnumerateFileSystemEntries (pathA))

{

fileSystemEntry = currentFile. Substring (pathA.Length);

if (fileSystemEntry == «» + MainForm. FileRemConf ||

fileSystemEntry.StartsWith («\~»))

continue;

fileA = new FileInfo (pathA + fileSystemEntry);

fileB = new FileInfo (pathB + fileSystemEntry);

if (Directory.Exists (pathB + fileSystemEntry))

{

int cmp = DirsCompare (fileA.FullName, fileB. FullName);

if (cmp ≠ 0)

{

if (cmp>0)

mode = SyncMode. AToB;

else

mode = SyncMode. BToA;

if (remA)

mode = SyncMode. AToB;

if (remB)

mode = SyncMode. BToA;

syncList.Add (fileSystemEntry, mode);

}

}

else if (!File.Exists (pathB + fileSystemEntry) ||

fileA.LastWriteTime ≠ fileB. LastWriteTime)

{

mode = SyncMode. AToB;

if (remB)

mode = SyncMode. BToA;

if (remA)

mode = SyncMode. AToB;

syncList.Add (fileSystemEntry, mode);

}

}

foreach (string currentFile in Directory. EnumerateFileSystemEntries (pathB))

{

fileSystemEntry = currentFile. Substring (pathB.Length);

if (fileSystemEntry == «» + MainForm. FileRemConf ||

fileSystemEntry.StartsWith («\~»))

continue;

if (!Directory.Exists (pathA + fileSystemEntry) && !File.Exists (pathA + fileSystemEntry))

{

mode = SyncMode. BToA;

if (remB)

mode = SyncMode. BToA;

if (remA)

mode = SyncMode. AToB;

syncList.Add (fileSystemEntry, mode);

}

}

}

if (syncList.Count > 0)

{

needShow = true;

if (autoSync)

//k

Synchronize ();

else

State = SyncState. WaitSolution;

}

else

State = SyncState. Ok;

remA = false;

remB = false;

needSync = true;

}

private static int DirsCompare (string dirNameA, string dirNameB)

{

foreach (string fileNameA in Directory. GetDirectories (dirNameA, «*»))

{

string fileNameB = fileNameA. Replace (dirNameA, dirNameB);

if (Directory.Exists (fileNameB))

{

int cmpRes = DirsCompare (fileNameA, fileNameB);

if (cmpRes ≠ 0)

return cmpRes;

}

else

return 1;

}

foreach (string fileNameB in Directory. GetDirectories (dirNameB, «*»))

{

string fileNameA = fileNameB. Replace (dirNameB, dirNameA);

if (!Directory.Exists (fileNameA))

return 1;

}

FileInfo fileA, fileB;

foreach (string fileName in Directory. GetFiles (dirNameA, «*.*»))

{

fileA = new FileInfo (fileName);

fileB = new FileInfo (fileName.Replace (dirNameA, dirNameB));

if (fileB.Exists)

{

if (fileA.LastWriteTime ≠ fileB. LastWriteTime)

return fileA.LastWriteTime.CompareTo (fileB.LastWriteTime);

}

else

return 1;

}

foreach (string fileName in Directory. GetFiles (dirNameB, «*.*»))

{

fileA = new FileInfo (fileName.Replace (dirNameB, dirNameA));

if (!fileA.Exists)

return -1;

}

return 0;

}

private void Synchronize ()

{

int compliteCount = 0;

progressMess = ««;

needShow = true;

List removeItems = new List ();

foreach (var item in syncList)

{

switch (item.Value)

{

case SyncMode. AToB:

syncDirsProcess (pathA, pathB, item. Key);

compliteCount++;

break;

case SyncMode. BToA:

syncDirsProcess (pathB, pathA, item. Key);

compliteCount++;

break;

case SyncMode. NoneNync:

success = false;

break;

}

if (success)

removeItems.Add (item.Key);

if (syncList.Count == 0)

break;

}

if (compliteCount == syncList. Count)

{

if (AllowAutoSync)

{

if (success)

State = SyncState. Ok;

else

State = SyncState. UnSuccess;

}

else

State = SyncState. Off;

}

else

State = SyncState. WaitSolution;

foreach (string item in removeItems)

syncList.Remove (item);

}

void syncDirsProcess (string pathA, string pathB, string file)

{

success = true;

try

{

if (Directory.Exists (pathA + file))

{

Directory.CreateDirectory (pathB + file);

foreach (string dirPath in Directory. GetDirectories (pathA + file, «*»))

syncDirsProcess (pathA, pathB, dirPath. Substring (pathA.Length));

foreach (string dirPath in Directory. GetDirectories (pathB + file, «*»))

{

if (!Directory.Exists (dirPath.Replace (pathB, pathA)))

Directory.Delete (dirPath, true);

}

foreach (string fileName in Directory. GetFiles (pathB + file, «*.*»))

{

if (!File.Exists (fileName.Replace (pathB, pathA)))

File.Delete (fileName);

}

foreach (string newFile in Directory. GetFiles (pathA + file, «*.*»))

{

FileInfo fileA = new FileInfo (newFile);

FileInfo fileB = new FileInfo (newFile.Replace (pathA, pathB));

if (!fileB.Exists || fileA. LastWriteTime > fileB. LastWriteTime)

{

backgroundSync.ReportProgress (1, fileA.Name);

CustomFileCopier fileCopier = new CustomFileCopier (newFile, newFile. Replace (pathA, pathB));

fileCopier.OnProgressChanged += new ProgressChangeDelegate (fileCopier_OnProgressChanged);

fileCopier.Copy ();

}

}

}

else

{

if (File.Exists (pathA + file))

{

backgroundSync.ReportProgress (1, (new FileInfo (pathA + file)).Name);

CustomFileCopier fileCopier = new CustomFileCopier (pathA + file, pathB + file);

fileCopier.OnProgressChanged += new ProgressChangeDelegate (fileCopier_OnProgressChanged);

fileCopier.Copy ();

}

else if (File.Exists (pathB + file))

File.Delete (pathB + file);

else

Directory.Delete (pathB + file, true);

}

}

catch (Exception)

{

success = false;

}

}

void reportProgress (object sender, ProgressChangedEventArgs e)

{

progressMess = e.UserState.ToString ();

}

void fileCopier_OnProgressChanged (double persentage,

ref bool cancel)

{

if (!success)

cancel = true;

progressPercent = persentage;

}

}

Приложение Д

Исходный код сущности коллекции синхропар

class SyncDirsList: List

{

StatusStrip statusStrip;

ListView listView;

SyncDirs currentTask;

Queue itemsInProcess;

bool stateChanged;

public SyncDirs CurrentTask

{

get { return currentTask; }

}

public bool StateChanged

{

get

{

bool bufVal = stateChanged;

if (bufVal)

stateChanged = false;

return bufVal;

}

set { stateChanged = value; }

}

internal Queue ItemsInProcess

{

get { return itemsInProcess; }

}

public ListView ListView

{

get { return listView; }

set { listView = value; }

}

public SyncDirsList (ListView listView, StatusStrip statusStrip)

{

this.listView = listView;

this.statusStrip = statusStrip; ;

itemsInProcess = new Queue ();

currentTask = new SyncDirs ();

stateChanged = true;

}

public new void Add (SyncDirs newItem)

{

base.Add (newItem);

ListViewItem parentItem = new ListViewItem (newItem.PathA);

ListViewItem.ListViewSubItem childItem = new ListViewItem. ListViewSubItem (parentItem, newItem. PathB);

ListViewItem.ListViewSubItem childItem2 = new ListViewItem. ListViewSubItem (parentItem, newItem. StateString);

parentItem.SubItems.Add (childItem);

parentItem.SubItems.Add (childItem2);

parentItem.Tag = newItem;

listView.Items.Add (parentItem);

stateChanged = true;

}

public new bool Remove (SyncDirs removingItem)

{

foreach (ListViewItem item in listView. Items)

if (item.Tag == removingItem)

{

if (removingItem == currentTask)

{

currentTask.Cancel ();

currentTask = new SyncDirs ();

}

listView.Items.Remove (item);

base.Remove (removingItem);

stateChanged = true;

return true;

}

return false;

}

public void UpdateList ()

{

if (!StateChanged)

return;

listView.Items.Clear ();

ListViewItem pIPathA;

string pathA, pathB;

foreach (SyncDirs item in this)

{

pathA = item. MarkA ≠ «»? «[» + item. MarkA + «] «+ item. PathA: item. PathA;

pathB = item. MarkB ≠ «»? «[» + item. MarkB + «] «+ item. PathB: item. PathB;

pIPathA = new ListViewItem (pathA);

ListViewItem.ListViewSubItem cIPathB = new ListViewItem. ListViewSubItem (pIPathA, pathB);

ListViewItem.ListViewSubItem cIState = new ListViewItem. ListViewSubItem (pIPathA, item. StateString);

pIPathA.SubItems.Add (cIPathB);

pIPathA.SubItems.Add (cIState);

pIPathA.Tag = item;

pIPathA.Selected = item. Selected;

listView.Items.Add (pIPathA);

}

}

public void SelSetState (SyncState state)

{

SyncDirs sDItem;

List items = new List ();

foreach (ListViewItem item in listView. SelectedItems)

items.Add (item);

foreach (ListViewItem item in items)

{

item.Selected = true;

sDItem = (SyncDirs)item.Tag;

if (sDItem == currentTask)

if (state ≠ SyncState. InProcess || state ≠ SyncState. Analyzing)

{

currentTask.Cancel ();

currentTask = new SyncDirs ();

}

else

continue;

sDItem.State = state;

sDItem.SyncList.Clear ();

}

stateChanged = true;

UpdateList ();

}

public string SyncsUpdate ()

{

SyncState state = SyncState. Ok;

foreach (SyncDirs item in this)

{

if (item.State ≠ SyncState. Off)

{

if (!item.DirsExists ())

{

if (item.State ≠ SyncState. NotAvailable)

{

item.State = SyncState. NotAvailable;

stateChanged = true;

}

}

else if (item.State == SyncState. NotAvailable)

item.State = SyncState. Analyzing;

}

if (item.State == SyncState. Analyzing || item. State == SyncState. Ok || item. State == SyncState. UnSuccess)

{

if (item.NeedSync)

{

item.State = SyncState. WaitProcess;

if (!itemsInProcess.Contains (item))

itemsInProcess.Enqueue (item);

stateChanged = true;

}

else if (item.State == SyncState. Analyzing)

{

item.State = SyncState. Ok;

stateChanged = true;

}

}

if (item.State == SyncState. WaitSolution)

{

state = SyncState. WaitSolution;

}

}

if (currentTask.State ≠ SyncState. InProcess && itemsInProcess. Count > 0)

{

currentTask = itemsInProcess. Dequeue ();

if (currentTask.State == SyncState. WaitProcess)

{

currentTask.Analyze ();

stateChanged = true;

}

}

if (currentTask.NeedShow)

{

if (currentTask.State == SyncState. Ok)

currentTask = new SyncDirs ();

stateChanged = true;

}

if (currentTask.State == SyncState. InProcess)

state = SyncState. InProcess;

UpdateList ();

StringBuilder programmState =

new StringBuilder («SyncDrive — «);

if (state ≠ SyncState. InProcess)

{

if (statusStrip.Items[1]. Text ≠ «» && !statusStrip.Items[0]. Visible)

{

statusStrip.Items[1].Text = «» ;

statusStrip.Items[3]. Visible = false;

(statusStrip.Items[3] as ToolStripProgressBar).Value = 0;

}

}

switch (state)

{

case SyncState. Ok:

programmState.Append («Все ок»);

break;

case SyncState. UnSuccess:

programmState.Append («Не все ок»);

statusStrip.Items[3]. Visible = false;

break;

case SyncState. InProcess:

programmState.Append («Идет синхронизация…»);

string progressMess = currentTask. ProgressMess;

double progress = currentTask. ProgressPercent;

if (progressMess ≠ «»)

{

statusStrip.Items[1]. Text = «Идет копирование: «+ progressMess;

if (progress > -1)

{

statusStrip.Items[3]. Visible = true;

(statusStrip.Items[3] as ToolStripProgressBar).Value = (int)progress;

}

statusStrip.Update ();

}

break;

case SyncState. WaitSolution:

programmState.Append («Выберите направление синхронизации»);

break;

}

return programmState. ToString ();

}

public void UpdatePaths (string oldDrive, string newDrive)

{

foreach (SyncDirs item in this)

{

if (item.PathA[0] == oldDrive[0])

item.PathA = newDrive[0] + item.PathA.Remove (0, 1);

if (item.PathB[0] == oldDrive[0])

item.PathB = newDrive[0] + item.PathB.Remove (0, 1);

}

}

}

Показать весь текст
Заполнить форму текущей работой