пятница, 3 октября 2014 г.

О подготовке архива с продуктом к публикации в сети

Зачастую, для распространения цифрового продукта авторы используют архивы, аргументами в пользу которых являются легкость создания, и пользователям удобно их распаковывать, но всегда ли?

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

Что может быть сложного в архиве?


Раньше я даже не задавался этим вопросом так как даже не мог предположить саму возможность, которая привела бы к постановке вопроса. Но это было давно, сейчас думаю иначе и несколько раз подумаю выбирая между архивом или standalone exe. Чтобы наглядней объяснить какая сейчас ситуация с компьютерной грамотностью, процитирую знакомого автора: "Многие пользователи, среди которых даже учителя информатики (!) не знали как распаковать архив! Двойным щелчком открывали всё подряд. Программа 7-Zip была тоже им незнакома. Уровень людей — преподаватели и руководители учебных заведений."

Если у вас продукт ориентирован на людей знающих что такое архиватор, то статья не для вас, а всем остальным Welcome!

Совет 1: используйте ZIP архивы


Поддержка ZIP архивов встроена в Windows уже более десяти лет, такие архивы открываются в проводнике как обычная папка. Если вы свой продукт будете распространять в zip, то вам с 99% вероятностью никогда не придется объяснять пользователю что нужно скачать архиватор, и установить его.

И еще, я не раз наблюдал как некоторые авторы пакуют продукт в rar и в инструкции по пользованию продуктом дают ссылку на WinRar. У данного подхода есть два главных недостатка:
  • Пользователь испытает огромный стресс пытаясь установить архиватор. Скорее всего он привлечет к этому процессу родственников более хорошо разбирающихся в компьютере.
  • Даже если он справится с установкой WinRar, при двойном клике по архиву, откроется WinRar с рекомендацией приобрести его, так как это не бесплатная программа. Тут реакция ясна, не успел открыть продукт, а опять нужно за что-то платить. Лохотрон!

Совет 2: избегайте русских букв в именах файлов


Все проблемы с русскими шрифтами возникают на не русскоязычных версиях Windows. Я видел много случаев когда после распаковки архивов имена файлов превращались в каракули. Также актуально если создать архив в Mac OS, а потом открыть его в Windows - будут крякозабры.

Совет 3: именуйте файлы для сортировки


Простое правило, если у вас есть, например, 20 файлов которые должны открываться по порядку (например в дистрибутиве идет документация с примерами). Вместо того, что бы давать им имена lesson 1, lesson 2, … , lesson 20 называйте их lesson 01, lesson 02,…, lesson 20. Нужно это для того, чтобы те пользователи, у которых проводник в режиме отображения файлов «Список» видели файлы в том порядке, в котором идут уроки. Это будет очень удобно. Если этого не сделать, то файлы будут расположены так: lesson 1, lesson 10, .., lesson 19, lesson 2, lesson 20, … , lesson 3, lesson 4, .. , lesson 9.

Совет 4: сообщайте пользователю о необходимости распаковки архива


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

Расскажу историю этого совета. Однажды ко мне обратился знакомый, просил помощи в саппорте его, мол уже все перепробовали - падает программа, то файла не хватает то еще что. В его архиве была программа Audacity, и некоторые другие, вылетали, саппорт перепробовал все что бы починить, ни отключение антивируса, ни проверка диска на ошибки не помогли. Я подключился по тимвьюверу к клиенту, чтобы посмотреть какие действия приводят к ошибке:
  • Скачивался файл по ссылке из письма distr.rar, он сохранялся в загрузки
  • Далее, в проводнике двойным кликом открывался этот файл
  • И таким же двойным кликом запускался Audicity файл (или любая другая программа из архива)
  • Собственно, запущенный файл сыпал ошибками.
На компьютере был установлен архиватор Power Archiver, который при запуске исполняемых файлов сначала извлекал этот файл во временный каталог, а потом запускает его. Естественно запущенная программа не может загрузить необходимых ей файлов, потому что они не распакованы — и сыпет ошибками.

Такая ситуация не возникла если бы на компьютере стоял WinRar (или TUGZip или HaoZip, потому что эти архиваторы при открытии файлов распаковывает весь архив), или в случае открытия файла встроенным zip архиватором Windows, который бы предупредил о невозможности запуска программ без распаковки всех файлов.

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

Совет 5: избегайте глубокой вложенности и длинных имен файлов в архиве


Многие архиваторы не поддерживают пути больше MAX_PATH (который 256), поэтому если в архиве пути набирают 100 символов, а потом такой архив пользователь пытается распаковать в c:\documnets and settings\мой супер пупер классный пользователь\рабочий стол\мои нужные программы\супер программа\ то ничего не выйдет из-за превышения общего пути в 256 символов.

Совет 6: Не используйте без надобности точку первым символом имени файла


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

Совет 7 для саппорта: если не понятно почему сломалось - проверяйте правильность распаковки архива


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

Совет 8 для программистов: добавьте в свое ПО детектирования запуска из архиватора


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


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

Для начала определим PID процесса который запустил нас:
uses
  TlHelp32;

//Получение PID'a родительского процесса
function GetParentProcessID(const ProcessId: DWORD): DWORD;
var
  hSnapshot: THandle;
  ProcessEntry: TProcessEntry32;
begin
  Result := 0;

  hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if hSnapshot <> INVALID_HANDLE_VALUE then
  try
    ProcessEntry.dwSize := SizeOf(ProcessEntry);
    if Process32First(hSnapshot, ProcessEntry) then
    repeat
      if ProcessEntry.th32ProcessID = ProcessId then
        Exit(ProcessEntry.th32ParentProcessID);
    until not Process32Next(hSnapshot, ProcessEntry);
  finally
    CloseHandle(hSnapshot)
  end;
end;
Зная PID родительского процесса получим полный путь до его исполняемого файла:
uses
  PSAPI;

//Получить путь к файлу процесса по PID'у
function GetPathFromPID(const PID: cardinal): string;
var
  HProcess: THandle;
begin
  Result := '';

  hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, PID);
  if hProcess <> 0 then
  try
    SetLength(Result, MAX_PATH + 1);
   SetLength(Result, GetModuleFileNameEx(hProcess, 0, PChar(Result), Length(Result)));
  finally
    CloseHandle(hProcess)
  end
end;
Теперь, собственно зная путь до файла родительского процесса мы можем написать простой детектор "плохих" архиваторов:
uses
  StrUtils;

//Проверка, запущено ли наше приложение из под архиватора который не умеет запускать
//исполняемые файлы из архива (не распаковывает доплнительные файлы во время запуска)
function IsRunUnderArchiver: Boolean;
var
  ParrentProcessPath: string;
  ParrentProcessName: string;
begin
  ParrentProcessPath := GetPathFromPID(GetParentProcessID(GetCurrentProcessId));
  ParrentProcessName := ExtractFileName(ParrentProcessPath);

  //WinRar, TUGZip и HaoZip исполныемые файлы запускают нормально,
  //поэтому их не детектим

  //PowerArchiver
  Result := SameText(ParrentProcessName, 'POWERARC.EXE');

  //WinZip
  if not Result then
    Result := ContainsText(ParrentProcessPath, 'WinZip');

  //7z
  if not Result then
    Result := StartsText('7z', ParrentProcessName);

  //IZArc
  if not Result then
    Result := SameText(ParrentProcessName, 'IZArc.exe');

  //PeaZip
  if not Result then
    Result := SameText(ParrentProcessName, 'peazip.exe');
end;

Ну и теперь мой пример. Изначально у меня был код который просто выводит ошибку если не найден файл:
if not FileExists(IprFile) then
begin
  IprNotFoundError := Format('Не найден файл %s'), [IprFile]);
  raise Exception.Create(IprNotFoundError);
end;
Теперь же его можно записать так:
if not FileExists(IprFile) then
begin
  IprNotFoundError := Format('Не найден файл %s'), [IprFile]);
  
  //Некоторые пользователи запускают стартовый файл из архиватора, но не все
  //архиваторы при запуске исполняемых файлов извлекают сопутсвующие файлы.
  //Проверим, запущены ли мы из архиватора, что бы напомнить об этом пользователю
  //сообщением
  if IsRunUnderArchiver then
    IprNotFoundError := IprNotFoundError + #13#10#13#10 +
      'Вы пытаетесь запустить видео файл прямо из архива. Для правильной его работы следует сначала извлечь все файлы из архива.';
  
  raise Exception.Create(IprNotFoundError);
end;

Вот так несколько строк кода могут спасти от лишнего обращения за помощью.

Заключение


На этом все, друзья, поменьше вам проблемных писем в саппорт!

Комментариев нет:

Отправить комментарий