четверг, 16 августа 2012 г.

Отслеживание путешествия пользователя по папкам

Сегодняшний пример возник на основании письма одного из подписчиков этого блога. Собственно вот текст этого письма:
---
Здравствуйте!
Хотел спросить возможно ли как то отловить событие открытия папки в ОС Windows? Как бы, чтоб если открывается на компьютере какая то папка тогда у меня в программе выводило путь к этой папке. Надеюсь Вы поняли ход моих мыслей, просто в голове составил как оно должно все работать, а вот объяснить не умею. Мне говорили что надо работать с Windows Shell Api чтоб работать с ОС, но по нем ничего нет. Если Вы знаете как подобное осуществить напишите мне пожалуйста, или где об этом можно прочитать. Спасибо!
---
А так-как сложилось сразу три вещи: вежливость спрашивающего, мое незнание как это сделать и свеже установленная восьмерка, на которой еще более интересно попробовать, то смотрите в подкат.
Ну что ж, давайте начнем.
Перво-наперво, приходит на ум идея поиспользовать класс FileSystemWatcher. Копируем в новое решение и... Оно не работает. Есть два требования:
Первое, заключается в том, что рассмотренный способ будет работать только с разделами отформатированными в NTFS.
Второе мене предсказуемо, чтобы заработал этот способ, придется поправить флаг в реестер. Открываем RegEdit с правами администратора и ищем в нем параметр с именем NtfsDisableLastAccessUpdate. По умолчанию стоит 1. Меняем на 0. Сразу предупреждаю, при этом акте вандализма у нас начнет винда тормозить при открытии папок с большим количеством файлов, поэтому по окончанию экспериментов не забудьте вернуть все как было.
Дальше все просто. Создаем консольное приложение вот с таким кодом:

static void Main(string[] args)
{  
            Run();
}
public static void Run()
{
           FileSystemWatcher watcher = new FileSystemWatcher();
           watcher.Path = @"d:\tmp";
           watcher.NotifyFilter = NotifyFilters.LastAccess;

           watcher.IncludeSubdirectories = true;
           watcher.Renamed += OnRenamed;
           watcher.Changed += OnChanged;
           watcher.Created += OnChanged;
           watcher.Deleted += OnChanged;
           watcher.EnableRaisingEvents = true;
    
           while (true) Thread.Sleep(100);
}
private static void OnChanged(object source, FileSystemEventArgs e)
{
           Console.WriteLine("\nFile: " + e.FullPath + " " + e.ChangeType);
}
private static void OnRenamed(object source, RenamedEventArgs e)
{
           Console.WriteLine("\nFile: {0} renamed to {1}", e.OldFullPath, e.FullPath);
}

И вот тут случается очередная затыка. События вызываются на переименование файлов, на изменение содержимого, на переименование дирректорий, но не на LastAccess - или по русски на последний доступ.
Проверил данную фишку и на Windows 7, и на Windows 8. Печалька.
Насколько я понимаю, единственное решение, которое будет хоть как то работать, это считывать свойство .
Итак, без всяких проверок на существование папок, окончание работы приложения и т.д., можно написать следующий код:
    class MyDirectoryWatcher
    {
        string _directoryPath;
        DateTime _lastAccessTime;

        public MyDirectoryWatcher(string p_directoryPath)
        {
            _directoryPath = p_directoryPath;
            _lastAccessTime = DateTime.Now;
            (new Task(TestDirectory)).Start();
        }
   
        private void TestDirectory()
        {
            while (true)
            {
                if ((new DirectoryInfo(_directoryPath)).LastAccessTime > _lastAccessTime)
                {
                    _lastAccessTime = DateTime.Now;
                    OnAccessed();
                }
                Thread.Sleep(100);
            }
        }


        public event Action<string> Accessed;

        protected void OnAccessed()
        {
            var handler = Accessed;
            if (handler != null)
            {
                handler(_directoryPath);
            }
        }
    }
   
    static void Main(string[] args)
    {
        MyDirectoryWatcher[] watchers = new MyDirectoryWatcher[3];
        watchers[0] = new MyDirectoryWatcher(@"D:\tmp");
        watchers[0].Accessed += Program_Accessed;
        watchers[1] = new MyDirectoryWatcher(@"D:\tmp\1");   
        watchers[1].Accessed += Program_Accessed;
        watchers[2] = new MyDirectoryWatcher(@"D:\tmp\2");
        watchers[2].Accessed += Program_Accessed;
        while (true) Thread.Sleep(100);
    }

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

18 комментариев:

  1. У меня маленький вопрос, что такое Program_Accessed?

    ОтветитьУдалить
  2. О_о
    Забыл, это метод вот такого вида:
    private void Program_Accessed(string dir)
    {
    Console.WriteLine(dir);
    }

    ОтветитьУдалить
  3. И еще один вопросик, а как сдать чтоб выводило путь к всем папкам на диске, а не только к отдельным которые указали в программе? Нужно запускать какой то цикл или что та в этом роде? Спасибо!

    ОтветитьУдалить
    Ответы
    1. Если взять диск C, то на нем тысячи папок и тупить будет просто безбожно. Если нужно отслеживать 10-100 папок, то последний вариант еще может и ничего так, при большем количестве папок будет все тупить. Если подскажите исходную задачу, то, вполне вероятно, можно будет предложить более интересное решение...

      Удалить
    2. Что собственно я хочу реализовать. Я создал программу в которой выбираешь каталог который надо защитить, вводишь пароль и после этого через проводник нельзя войти в наш каталог, также можно и снять защиту также выбираем наш каталог (через программу) вводим наш пароль и если пароль введен правильно то защита снимается. Но я хотел бы чтоб когда каталог зашифрован и его пытаются открыть через проводник в них выскакивало окно с просьбой ввести пароль.

      Удалить
    3. Тарас, хотите хороший совет? Архивируйте эти папки с паролем и будет вам счастье ))

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

      Удалить
    5. Приветствую ) Можно же постепенно строить TreeView по мере погружения в дебри папок ;) Кстати, интересно, как этот наблюдатель будет работать с файловыми менеджерами.

      Удалить
    6. Тарас, да ладно, у вас нет интересных задач? ))
      Давайте я вам подину идеек. Что вам интересно? Базы данных, пользовательские интерфейсы, игры или еще что-то?

      Удалить
    7. Та мне все интересно, сделать будь что, лишь бы от него была польза) А не так как допустим сделал браузер или проигрыватель, а пользы от этого? никакой! этих браузеров или проигрывателей как звезд на небе)
      Мне нравится WPF, но на нем я не умею работать (пока). Питался писать игры на XNA -прикольно, даже понравилось.
      Подкиньте мне идею что сделать лишь бы это было интересно и приносило хоть кому то пользу)

      Удалить
    8. Ну если WPF, то можно:
      1. Рисовалку Mindmap-ов.
      Здесь все просто, про них можно почитать на вики, ну и у меня есть пара идей, что бьыло бы полезно.
      2. Приложение для хранения информации в виде сети.
      Здесь чуть сложнее. Все время идет большой поток входной информации, ее необходимо хранить в виде удобном для дальнейшего поиска и обработки. Например, идут параметры серверов, их айпи, пожелания по развертыванию на них сервисов, заводятся учетки на разных сервисах (логины/пароли), приходят приглашения на мероприятия, появляются новые идеи, попадаются класные изображения на различные тематики, статьи и т.д. Вот хочется приложение, в котором все это можно будет собирать и искать. Поиск как вариант можно сделать по тегам, причем не единичным, а с возможность выбора нескольких...
      3. Приложение контроля удаленных компьютеров и сервисов на них развернутых (понятно есть систем центр, но уж больно задачка интересная).

      Удалить
    9. О_о ого, да вижу что нужно снова садится за учебники и изучать WPF. Мой уровень знаний в программировании слишком мал для разработки подобных программ(!

      Удалить
    10. Так это и замечательно, я когда начинаю изучать новые технологии сначала придумываю под нее задачу, а потом изучаю и параллельно пытаюсь применять полученные знания. Эффективность изучения сразу становится намного выше ))

      Удалить
  4. Кстати, как впечатления от 8ки? :)

    ОтветитьУдалить
    Ответы
    1. Нормальное. У меня уже установлен был RC. Первый день непревычно, потом (после возврата на семерку) было еще более непревычно.

      Удалить
    2. Да впечатления от 8 очень приятные, быстро работает все программы что и на 7 работали работают и на 8, одним словом Microsoft постарался)

      Удалить