7-Zip из .NET или как я делал open source проект на CodePlex



    Эта статья с одинаковым успехом может быть отнесена к блогам ".NET", «Open source» и «Я пиарюсь». После того. как я написал материал, стало ясно, что больше всего в ней «Open source»… Просьба сильно не бить, если я ошибся.

    Итак, ниже пойдет рассказ о моем полуторагодичном опыте разработки библиотеки SevenZipSharp с открытыми исходниками, выложенной на CodePlex в феврале 2009. Эта библиотека — обертка над 7-Zip, позволяющая с легкостью его использовать в .NET.

    Использование SevenZipSharp

    В библиотеке два главных класса — SevenZipExtractor и SevenZipCompressor. Шаблон использования первого:
    // Синхронная распаковка
    using (var extr = new SevenZipExtractor(@"путь\к\архиву"))
    {
      extr.Extracting += DoExtractingEvent();
      extr.ExtractArchive(@"куда\распаковывать");
      DoFinishEvent();
    }

    // Асинхронная распаковка
    var extr = new SevenZipExtractor(@"путь\к\архиву");
    extr.Extracting += DoExtractingEvent();
    extr.ExtractionFinished += (s, e) => { DoFinishEvent(); extr.Dispose(); extr = null; };
    extr.BeginExtractArchive(@"куда\распаковывать");


    * This source code was highlighted with Source Code Highlighter.
    Шаблон использования второго:
    // Синхронная упаковка
    var cmpr = new SevenZipCompressor();
    cmpr.CompressDirectory(@"путь\к\пакуемой\папке", @"имя\архива");
    DoFinishEvent();
    cmpr = null;

    // Асинхронная упаковка
    var cmpr = new SevenZipCompressor();
    cmpr.CompressionFinished += (s, e) => { DoFinishEvent(); cmpr = null; }
    cmpr.BeginCompressDirectory(@"путь\к\пакуемой\папке", @"имя\архива");


    * This source code was highlighted with Source Code Highlighter.

    Не хочу превращать статью в документацию по SevenZipSharp, так что просто перечислю ее некоторые возможности:
    • Полная поддержка многопоточности
    • Оборачиваются все форматы архивов, поддерживаемые 7-Zip-ом
    • Неприхотливость к оборачиваемой библиотеке — никаких жестких привязок
    • Поддержка многотомных архивов при распаковке и упаковке
    • Распаковка большинства SFX архивов
    • Весь код тщательно снабжен комментариями
    Как все началось

    В феврале 2009 года мне понадобилось работать с 7-zip архивами в одном из платных проектов, над которым я трудился. Несколько дней я безуспешно искал готовое удобоваримое решение, но не нашел ничего лучше этой статьи на CodeProject. Зато прочитал много сообщений, где люди жаловались на его отсутствие. И тогда, собрав волю в кулак и отталкиваясь от найденной статьи, я мужественно пошел вперед принялся писать свою реализацию обертки над 7-Zip. Я решил выложить код на недавно открывшемся CodePlex под лицензией LGPLv3. Поначалу работа кипела, и я выпускал релиз за релизом раз в несколько дней (вы можете в этом убедиться сами в разделе «Other downloads» на странице загрузок). Потом мой пыл несколько поугас и я стал стабилизировать код. В сентябре 2009 релизы перестали выходить часто (я женился) и с тех пор я поддерживаю проект, как могу.

    Рассматривался вариант с компиляцией 7-Zip в смешанную сборку (mixed assembly) с флагом /clr. Этот вариант был отвергнут, т.к. во-первых, интерфейс получился бы низкоуровневый и не пригодный для быстрого использования и все равно пришлось писать «добавку», а во-вторых, чтобы собрать код с флагом /clr:pure, требовалось бы переписывать много кода, и unmanaged части все равно остались.

    Когда SevenZipSharp только появился, мне хотелось рассказать о нем потенциально заинтересованным пользователям-разработчикам. Я оставлял краткое описание библиотеки везде, где было можно: в ответах на вопросы StackOverflow, на программистских форумах (в т.ч. MSDN, Channel 9), в комментариях к той самой статье на CodeProject и даже на английской Wikipedia. Это все принесло результаты, и вскоре поисковые выдачи Google вышли по траффику на первое место. Думаю, рекламировать свои проекты должны все, иначе большинство попросту не узнает об их существовании. Эффективность рекламы оценивается по статистике загрузок и посещений, которая публично доступна.

    SevenZipSharp и 7-Zip

    Как многие знают, 7-Zip написан на C++ с небольшим количеством C и пресловутой ассемблерной функцией, вычисляющей CRC32, которую Игорь Павлов реализовал для x86, x86-64 и ARM. Какая либо документация по коду отсутствует, что в стиле русских программистов, участвующих в open source движении. Кода много, он совсем не прост и требуется некоторое время, чтобы разобраться в изобилии define-ов, интерфейсов и классов. Реализации алгоритмов сжатия называются кодеками (Codecs). Кодеки стандартным способом встраиваются в библиотеку, как подключаемые модули протоколов в мессенджерах ala Miranda/Pidgin. Архитектура 7-Zip неотделима от COM; именно это препятствует развитию p7zip — 7-Zip для POSIX систем, которым также занимается Игорь Павлов. В p7zip COM заменен костылем, который симулирует его работу, попутно объявляя половину типов windows.h. Сами алгоритмы написаны безупречно и очень стабильны, однако верхние уровни, как вы уже догадываетесь, оставляют желать лучшего. Если бы автор начал писать 7-Zip сейчас, думаю, он придумал архитектуру ядра более понятную, универсальную и портируемую, в идеале — на языке вроде C# или Java, хоть даже на Python (ну не годятся для этих целей плюсы, чего уж там).

    Кстати, 7-Zip для конечных пользовтелей (инсталляция, которая качается с 7-zip.org) собирается Visual Studio 6 образца начала века. Файлы решений успешно преобразовываются в форматы VS2008/2010, и после замены компилятора C/C++ на более новый и активации всех флагов оптимизации (да-да, моя основная профессия — компиляторщик), а также использования профиля достигается ускорение около 15% (LZMA/LZMA2). На заметку…

    Вот как SevenZipSharp оборачивает 7-Zip. Через COM-овский CreateObject создается объект, поддерживающий указанный интерфейс (IInArchive, IOutArchive). Из этого объекта дергаются нужные функции, и достигается желаемый результат (например, IInArchive.Extract(...)). Во время длительных операций из unmanaged кода вызываются managed callback-и, и это приводит к проблеме, которую я не сразу осознал — обработка ошибок. Например, из-за ошибки в callback-е или исключении в вызываемом callback-ом пользовательском событии исполнение операции падает без предупреждений и какой-либо вразумительной информации, кроме странного 32-битного кода ошибки. Я решил обернуть все callback-и try/catch-ами и заносить все возникшие исключения в стек ошибок, который в случае неудачи показывается пользователю. Если есть более изящное решение, прошу о нем рассказать.

    Попытки рубить с плеча переписать весь код 7-Zip на C# энтузиастами предпринимаются регулярно, но ни одна не ушла дальше обсуждений. Переделывать алгоритмы с C++ на C# не выгодно: затраченные усилия и падение в скорости не окупаются кроссплатформенностью и религией, а переписать ядро с учетом всех тонкостей может только сам Игорь Павлов. Не буду голословен: LZMA на C#/.NET из LZMA SDK по замерам работает в 4 раза медленнее unmanaged алгоритма. Поэтому, пожалуй, лучшим в такой ситуации было сделать обертку с понятным и простым интерфейсом.

    В один прекрасный момент мне захотелось заставить работать SevenZipSharp под Mono (GNU/Linux). И тогда проблема привязанности 7-Zip к COM проявила себя во всем великолепии. Необходимо было заново написать низкоуровневую часть библиотеки почти с нуля. Т.к. код 7-Zip, как я уже писал, специфичный, инструменты для автоматической обертки вроде SWIG оказались бесполезными, а чтобы они вообще заработали, мне пришлось сначала пройтись по всему коду препроцессором и убрать 10-ти этажные define-ы. В настоящее время я потихоньку пишу независимую от COM обертку.

    Разработка

    Наверное, многие начинающие C# разработчики повторяют одни и те же ошибки, и я не являюсь исключением. Когда мне стало известно о FxCop и StyleCop, я сразу попробовал их использовать. Казалось бы, логично поддерживать код библиотеки в хорошем состоянии. Однако, StyleCop по умолчанию выдает слишком много предупреждений, а настраивать его мне не хотелось, и он был сразу отброшен. FxCop использовался некоторое время, но в один прекрасный момент я поймал себя на мысли, что на элементарные изменения кода я трачу слишком много времени, чтобы соответствовать правилам, которые, по сути ни на что не влияют. Вывод, который я для себя сделал — эти инструменты важны для выработки индивидуального стиля программирования, но разработчикам-одиночкам увлекаться ими не стоит.

    Изначально SevenZipSharp писался на Visual Studio 2008 и работал под 2-ым фреймворком. Даже тогда я понимал, что чем меньше версия .NET, тем меньше проблем будет использующим библиотеку. Жаль, что многие разработчики на CodePlex этого не понимают, начинают писать код, используя ультрасвоременные возможности .NET 4, и потом удивляются, почему у них так мало загрузок. Потом я узнал, что в Windows Mobile < 7 есть полноценный COM, и за несколько дней портировал SevenZipSharp на эти мобильные системы. Если кто-то не знает, фреймворки обычные и compact имеют ряд различий, и код без небольших изменений не будет собираться, а тем более работать. Поддерживать две почти идентичные ветки я считал неразумным, и решил эту проблему множественными #if/#else/#endif (стандартный подход в исходниках на C++).

    Когда вышел Visual Studio 2010/C# 4, я обнаружил, что фичи новой версии языка можно эффективно применить к коду (например, появившиеся опциональные параметры устраняют 10+ перегрузок единственного логического метода). Для сохранения обратной совместимости я снова применил #if/#else/#endif. Код понемногу стал превращаться из изящных классов и ветвистого монстра. А когда пришла идея портировать SevenZipSharp на Mono, некоторые файлы с кодом я все-таки раздвоил, т.к. иначе сам бы через пару недель не смог в нем разобраться. В итоге, передо мной во всей красе встала проблема поддержки разных платформ и фреймворков в одном единственном файле. Пример:
    #if !DOTNET20
        /// <summary>
        /// Unpacks the whole archive asynchronously to the specified directory name at the specified priority.
        /// </summary>
        /// <param name="directory">The directory where the files are to be unpacked.</param>
        /// <param name="eventPriority">The priority of events, relative to the other pending operations in the System.Windows.Threading.Dispatcher event queue, the specified method is invoked.</param>
    #else
        /// <summary>
        /// Unpacks the whole archive asynchronously to the specified directory name at the specified priority.
        /// </summary>
        /// <param name="directory">The directory where the files are to be unpacked.</param>
    #endif
        public void BeginExtractArchive(string directory
    #if !DOTNET20
          , DispatcherPriority eventPriority
    #if CS4
          = DispatcherPriority.Normal
    #endif
    #endif
    )
        {
          SaveContext(
    #if !DOTNET20
            eventPriority
    #endif
          );
          (new ExtractArchiveDelegate(ExtractArchive)).BeginInvoke(directory, AsyncCallbackImplementation, this);
        }


    * This source code was highlighted with Source Code Highlighter.

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

    Бонусы

    От участия в open source проекте на CodePlex появились неожиданные приятные сюрпризы. Во-первых, я заметил, что компания JetBrains, создатель ReSharper, выдает бесплатные лицензии разработчикам программного обеспечения с открытым исходным кодом. Я попытал счастья и не пожалел — мне действительно выдали лицензию. ReSharper оказался незаменимым помощником в написании кода, и я всем его советую. Во-вторых, с недавнего времени CodePlex показывает рекламу на страницах проектов (по желанию их владельцев). Доход от рекламы можно либо жертвовать на благие цели, либо присваивать себе. Я выбрал второй вариант, и получаю около 10$ в месяц. В-третьих, когда SevenZipSharp обрел популярность, мне стали предлагать спонсорство компании, разрабатывающие полезные инструменты для C#/.NET программистов, такие как NDepend и SciTech .NET Memory Profiler. В-четвертых, небольшие деньги приносит кнопка «Donate». Никто пока больше 10$ не жертвовал, но даже это приятно и стимулирует.

    Воодушевляет, когда в письмах благодарят за библиотеку, сообщают, что используют ее в реальных и весьма известных проектах (например, Stardock). Иногда мне приходят письма от людей, с предложениями начать работать над библиотекой вместе. Человек обычно полон энтузиазма, уверяет, что вместе будет здорово и т.п. После ответного в письма, в котором я пишу, что сразу никому пароль от SVN не дам, спрашиваю, а что человек вообще умеет, и описываю примерные планы развития проекта на будущее, никто со мной пока не связывался. Мне это кажется странным, возможно, психологию таких людей мне объяснят в комментариях.


    Типичное письмо без продолжения

    Поскольку речь зашла о людях, расскажу о публике на CodePlex. Несколько раз мне жертвовали код, причем всего один раз — по правилам, через патч. Порой давали дельные советы, подсказывали, что можно сделать лучше. Очень приятно, когда ошибки исправляешь не ты, а другие пользователи, и делятся потом багфиксом. Однако часто о баге заявляют не в Issue Tracker, а в обсуждения (Discussions), даже если баг очевидный. Приходится регулярно вчитываться в вопросы и решать, чей это косяк, библиотеки или криворукого пользователя. Впрочем, иногда появлялись «участники», заводящие сразу с десяток багов, из которых в лучшем случае пара действительно стоящие, а остальные являются просьбами добавить лишнюю функциональность, которая кроме самих «участников» никому не нужна. Бывает, что завел человек баг вроде «не работает распаковка», спрашиваешь у него в комментариях, какая версия библиотеки, как воспроизвести ошибку, а тот уже давно забыл про SevenZipSharp и не отвечает. Совсем.

    Отдельно веселят люди, оставляющие оценки (звездочки на CodePlex, от 1 до 5). Бесит, когда ставят 2 и не объясняют почему. Впрочем, также бесит когда ставят 2 и пишут, что мол вообще ничего не работает и библиотека ваша говно. К счастью, с SevenZipSharp это происходит редко, в отличие от других популярных проектов, которые, я уверен, не заслуживают таких оценок.

    Итоги

    Оглядываясь назад, я вижу, что связался с SevenZipSharp не зря. Получены и бесценный опыт, и некоторые выгоды. Если вы спросите, а стоит ли разрабатывать свой open source проект «для души», я без раздумий отвечу — конечно!

    Спасибо за внимание.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 46

      +13
      Благодаря таким людям как вы, держится движение Open Source. Спасибо вам.
      P.S. Сам я C# не люблю :P
        0
        Спасибо!
          0
          > Благодаря таким людям как вы, держится движение Open Source
          И свободного ПО:
          > Я решил выложить код на недавно открывшемся CodePlex под лицензией LGPLv3.
          А то open source под разными лицензиями бывает :)
            –5
            А вы кроме GPL других опен-сорс лицензий не знаете?
              +4
              Очень интересно, как вы трактовали мой комментарий?
                0
                Так, будто вы считаете, что кроме GPL других опен-сорс лицензий не бывает.
                  0
                  И где же вы это увидели?
                    –1
                    >> Я решил выложить код на недавно открывшемся CodePlex под лицензией LGPLv3.
                    >А то open source под разными лицензиями бывает :)
                      +2
                      И? Развивайте мысль.
                        0
                        Ничего. Видимо, я вас не так понял. Просто смысл приведенной мной цитаты я понимаю именно так. Хотя вы наверное имели ввиду нечто другое :-)
            +13
            М… Боюсь сказать, но по моему опыту Open Source держится на очень больших деньгах корпораций вложеных чтобы заработать еще больше денег. А такие люди — это только маленькая часть
            0
            > Типичное письмо без продолжения

            Человек прямо спросил, чем вам помочь. Сказали бы, что сейчас нужно добавить в библиотеку, что отладить или какой баг исправить.
              +2
              Так я об этом ему подробно и написал! И о планах рассказал, и какие есть известные баги. Тишина…
                +1
                На моем (не очень большом) опыте всегда было так: письма с предложениями что-то поделать ни к чему не приводят. Работает только модель «взял и сделал», возможно, предварительно обсудив. Если человек не знает, что бы сделать, а хочет просто помочь проекту, то заинтересованности у него хватает совсем ненадолго. Другое дело — люди, которые используют проект, сталкиваются с какими-то сложностями, смотрят код, находят решение и о нем рассказывают/высылают патч.
                  +1
                  Я тоже, когда появляется желание поучаствовать в опенсорс проекте, рассылаю подобные письма, потом выбираю тот проект, который больше всего в данный момент подходит под мои знания и текущие возможности.
                  Так что ничего крамольного не вижу, тут же нет обязаловки сразу подвязываться после первого же письма.
                +2
                Шикарно, теперь есть альтернатива ставшему уже привычным SharpZipLib.
                Я помню, когда писал nbox (тоже на codeplex'e лежит), мне было нужно сжимать и разжимать блоки данных, я тогда чистое LZMA api использовал. Значит, теперь можно и архивы создавать, good job!
                  –1
                  Было бы гораздо полезнее написать pure managed вариант.
                    0
                    Спорно. Да и в тексте статьи про это сказано.
                      +2
                      Вы про это?
                      >> затраченные усилия и падение в скорости не окупаются кроссплатформенностью и религией

                      В таком случае в статье написана чушь. Вся суть в CAS, а не скорости или религии. И кстати скорость SharpZipLib как и встроенных GZipStream/DeflateStream особых нареканий не вызывают.
                        0
                        SharpZipLib даже на Compact Framework работает вполне сносно.
                          0
                          Ну вот тем более.
                            0
                            Можете почитать, что мне пишут пользователи-владельцы серверов на ASP.NET. Даже за 10% падение скорости они ругаются, причем постоянно мне об этом напоминают. Вы думаете, я не проводил тестов? LZMA алгоритм на чистом шарпе работает в 4 (!) раза медленне аналога из 7-Zip. Deflate гораздо проще LZMA, и всегда работал быстро.
                              +2
                              Ага, а исполнение unmanaged кода на ASP.Net хостинге это фигня :-)
                                0
                                Объясните это моим заграничным друзьям :)
                          0
                          Отбросим скорость. См. статью от ABBYY «Как не переносить код на 64 бита» habrahabr.ru/company/abbyy/blog/101560. Там пишут в том числе о том, что чем меньше средств тратится на решение проблемы, тем оно лучше. У меня было несколько свободных часов в неделю, т.е. если бы мне, как и большому количеству студентов из MSDN Channel 9, пришла в голову идея написать managed 7-zip, то
                            0
                            Прошу прощения…
                            То
                            1)я бы через год все равно не дописал что хотел
                            2)вряд ли я себя хотя бы через месяц заставил продолжать работу
                              0
                              Поверьте, managed 7-zip с нуля — титанический труд
                                0
                                Теперь давайте ещё раз проанализируем фразу «затраченные усилия и падение в скорости не окупаются кроссплатформенностью и религией». Видимо надо было написать «Необходимых человеческих ресурсов у меня не было, к тому же мои заграничные друзья серийные маньяки фанатеющие от мегагерцеф». Вместо «объективных» причин были бы субъективные. А объективные причины просты — пока существуют ASP.Net shared хостинг и Silverlight в частности и недоверенные среды исполнения вообще, чисто усправляемые решения востребованы.
                        • НЛО прилетело и опубликовало эту надпись здесь
                            0
                            а что скажете о библиотеке DotNetZip (http://dotnetzip.codeplex.com/)? Мне хватает ее возможностей, так как пользуюсь только ZIP.
                            Есть ли у вашей разработки преимущества перед ней, которые могут заинтересовать?
                              0
                              Поддержка всех форматов, с которыми работает 7zip, а это как минимум родной 7z и очень популярный в наших широтах rar.
                                +1
                                Скорость работы с архивами, и это имхо главный плюс
                                0
                                Приятно читать такие статьи — после них сразу захлестывает волна энтузиазма и хочется тоже что-нибудь полезное сделать.
                                /me запустил MSVS2010
                                  +9
                                  > /me запустил MSVS2010
                                  И закрыл по окончанию загрузки?
                                  0
                                  Странно, а разве в LZMA SDK нет исходников на шарпе? По крайней мере еще в прошлом году я их использовал. С точки же зрения Open Source статья однозначно интересна, спасибо.
                                    0
                                    Там есть реализация LZMA… На этом шарп кончается :) Думаю, из-за различия в скорости managed LZMA и unmanaged дальше двигаться не стали. Прошло два года, а код не менялся
                                    0
                                    А мысль сделать из этого стартап не рассматривалась?
                                      +5
                                      Гм, нет… Честно говоря, не задумывался даже. Что здесь монетизировать? :)
                                      0
                                      Под Linux/Mono работает?
                                        0
                                        Скоро начнет. Через пару недель
                                          0
                                          Очень жду когда будет, и будет достаточно быстро и безглючно. Спасибо Вам.
                                            0
                                            Не нашел на SVN заветный файлик — SevenZip.Mono.COM :(
                                          0
                                          Мне кажется что если у тебя код не managed то нет смысла компилировать в .net нету.
                                          Можно просто скомпилить DLLку и ее вызывать из .net
                                          В целом стремления это прекрасно!
                                          >>А мысль сделать из этого стартап не рассматривалась?
                                          5 баллов! Это квинтэссенция хабрахабр!
                                            +1
                                            А если под видом 7z.dll будет находиться некий зловредный код, который в точности будет содержать интерфейсы правильного кода, но выполнять действия далеко не упаковки\распаковки?
                                            Проверяет ли код Вашего проекта аутентичность вызываемой библиотеки 7z.dll?
                                              0
                                              Мой код способен работать с любой 7z.dll. Например, с custom build только с теми кодеками, которые нужны, и вдобавок оптимизированным компилятором. Как тут проверять? Если пользователь библиотеки захочет, то сам может привязываться к определенной 7z.dll (явно указывать, откуда ее загружать) и проверять ее, например, по md5.
                                              0
                                              Приятно видеть сколько опыта вы получили, работая над этим проектом. Это конечно не стартап, но первый опыт предпринимательства. Так держать.

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

                                              Самое читаемое