Total Commander Forum Index Total Commander
Форум поддержки пользователей Total Commander
Сайты: Все о Total Commander | Totalcmd.net | Ghisler.com | RU.TCKB
 
 RulesRules   SearchSearch   FAQFAQ   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Уникальность HANDLE в архиваторном плагине

 
Post new topic   Reply to topic    Total Commander Forum Index -> Написание плагинов для Total Commander printer-friendly view
View previous topic :: View next topic  
Author Message
Mowgli



Joined: 25 Jun 2009
Posts: 11

Post (Separately) Posted: Thu Jun 25, 2009 23:41    Post subject: Уникальность HANDLE в архиваторном плагине Reply with quote

Пишу архиваторный плагин. Использую VC, С++, STL. Имеется класс, инкапсулирующий работу с файлом архива. Т.е. экземпляр этого класса - это интерфейс к конкретному файлу. Функцию OpenArchive пишу примерно так (очень упрощённый код):

Code:
HANDLE __stdcall OpenArchive(tOpenArchiveData* ArchiveData)
{
   TArch * arch;
   try
   {
      arch = new TArch(std::string(ArchiveData->ArcName));
   }
   catch (TOblomVyshel ex)
   {
      ArchiveData->OpenResult = E_BAD_DATA;
      return 0;
   }
   return arch;
}

Теперь непонятка. В документации написано, что возвращаемое функцией OpenArchive значение должно быть уникально. Функция возвращает тип HANDLE. Это дескриптор объекта виндовс, что наводит на мысль о требовании уникальности этого числа в системе вообще.
Как видно из моего кода возвращается обычный указатель, полученный оператором new. Он, само-собой, не уникален в глобальном смысле и даже может пересечься со значениями каких-либо системных дескрипторов в пределах процесса. Хотя как указатель он уникален между всех других указателей в пределах процесса (и соответственно между всеми экземплярами работающих функций плагина).
Собственно вот так, как показано, у меня работает и пока без проблем. Но хотелось бы до конца понять мутную фразу из руководства насчёт уникальности и по возможности предотвратить потенциальные проблемы.

Я как себе представляю логику работы TC:
- При открытии файла TC вызывает функцию OpenArchive и передаёт ей имя архива, в ответ получает некое число. Число запоминается и связывается с открытым файлом.
- Для всех дальнейших операций функциям плагина передаётся это число, вплоть до закрытия архива.
- Если одновременно с первым открывается другой файл, то для него TC получает и передаёт потом другое число.

Исходя из такой логики уникальность требуется исключительно между одновременно вызванными функциями плагина. Это могут быть даже не указатели, а скажем числа 0, 1, 2 и т.д.
Но так ли это на самом деле? В этом и вопрос.


Last edited by Mowgli on Fri Jun 26, 2009 03:32; edited 1 time in total
Back to top
View user's profile Send private message
VadiMGP



Joined: 21 Mar 2007
Posts: 1625

Post (Separately) Posted: Fri Jun 26, 2009 01:36    Post subject: Reply with quote

То, что ты делаешь - правильно. HANDLE должен быть уникальным для тебя. В том смысле, что когда ТС вызывает твою функцию и передаёт ей HANDLE ранее полученный от тебя же, ты должен быть способен правильно понять о каком архиве идет речь.
Mowgli wrote:
Исходя из такой логики уникальность требуется исключительно между единовременно вызванными функциями плагина. Это могут быть даже не указатели, а скажем числа 0, 1, 2 и т.д.
Непонятно что такое "единовременно вызванные функции плагина", но в принципе, ты логику описал верно.
Back to top
View user's profile Send private message
Mowgli



Joined: 25 Jun 2009
Posts: 11

Post (Separately) Posted: Fri Jun 26, 2009 03:47    Post subject: Reply with quote

VadiMGP wrote:
Непонятно что такое "единовременно вызванные функции плагина", но в принципе, ты логику описал верно.

Ошибка вышла. "Одновременно" конечно же (в сообщении поправил).
Хотя вот грызут меня сомнения. Очевидно, что одновременно несколько открытых архивов - это совершенно нормальная ситуация. А вот возможна ли ситуация, когда одновременно одним плагином распаковывается несколько разных архивов? Интерфейс плагина (программный) такую ситуацию вроде бы не исключает. Надо лишь делать все функции реентерабельными. А вот пользовательский интерфейс самого TC похоже такого не допускает.
Ну по крайней мере у меня не вышло одновременно из двух панелей запустить распаковку.
Back to top
View user's profile Send private message
VadiMGP



Joined: 21 Mar 2007
Posts: 1625

Post (Separately) Posted: Fri Jun 26, 2009 10:49    Post subject: Reply with quote

Mowgli wrote:
А вот возможна ли ситуация, когда одновременно одним плагином распаковывается несколько разных архивов?

Наверно можно создать такую ситуацию. В комбинации с WDX плагинами.
WDX плагин может быть вызван из разных потоков. А уже WDX плагин может обратиться к архиватору.
Но почему тебя волнует именно распаковка? Плагин может выполнять и другие операции - упаковка, тест...
Back to top
View user's profile Send private message
Worros



Joined: 19 Jan 2007
Posts: 1123

Post (Separately) Posted: Fri Jun 26, 2009 18:34    Post subject: Reply with quote

Mowgli wrote:
А вот возможна ли ситуация, когда одновременно одним плагином распаковывается несколько разных архивов?
По-моему невозможна. Гислер сделал интерфейс архиваторных плагинов на основе своего кода по взаимодействию с unrar.dll . Она реэнтерабельности не обеспечивала, поэтому возможность запустить распаковку в бэкграунде была сознательно исключена. Если WDX (или другое ПО, делающее попытки использовать интерфейс арх. плагинов) будет заниматься самодеятельностью и напрямую обращаться к WCX - это нарушение стандарта ТС.
_________________
педофилия, каннибализм, бетономешалка !!!
Back to top
View user's profile Send private message
VadiMGP



Joined: 21 Mar 2007
Posts: 1625

Post (Separately) Posted: Fri Jun 26, 2009 18:53    Post subject: Reply with quote

Worros wrote:
это нарушение стандарта ТС
Может и нарушение, но во-первых, стандарт этот неписаный, то есть это скорее традиция, а во-вторых, такого ПО, которое напрямую обращается к плагинам, уже вагон и маленькая тележка.

Суровость ТС-шных стандартов компенсируется необязательностью их соблюдения.

ЗЫ. Отсутствие потокоустойчивости unrar.dll это исключительно свойство unrar.dll. Почему из-за этого были запрещены операции в фоне вообще для всех архиваторов - непонятно. Гислеру не раз предлагали ввести еще один бит в свойствах архиваторов - потокоустойчивость. Лет пять назад он даже согласился и пообещал добавить этот бит.
Вот со дня на день ждем-с... Smile
Back to top
View user's profile Send private message
Mowgli



Joined: 25 Jun 2009
Posts: 11

Post (Separately) Posted: Fri Jun 26, 2009 23:15    Post subject: Reply with quote

VadiMGP wrote:
Наверно можно создать такую ситуацию. В комбинации с WDX плагинами.

Worros wrote:
По-моему невозможна. ...это нарушение стандарта ТС.

VadiMGP wrote:
Лет пять назад он даже согласился и пообещал добавить этот бит.
Вот со дня на день ждем-с... Smile

Какую животрепещущую тему я затронул =) Вот что значит взгляд со стороны.
Ну в любом случае я понял, что иду верной дорогой.
Quote:
Но почему тебя волнует именно распаковка?

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

Меня уже другое интересует.
Вот например описание функций OpenArchive, CloseArchive, ReadHeader и ProcessFile весьма неоднозначное и мутное. Кроме того:
- во-первых, никак нельзя описывать работу этих функций по-отдельности, поскольку они являются частью одного алгоритма.
- во-вторых, надо их описывать по-возможности в привязке к происходящему со стороны пользовательского интерфейса.

Я сейчас напишу своё понимание этого вопроса (только для случая распаковки), и прошу подтвердить или поправить.

Главный факт: открытие архива и распаковка из него - это разные и независимые циклы работы плагина. Из документации это можно понять только косвенно.
Вкратце, цикл это:
- в начале разовый вызов OpenArchive
- вызов ReadHeader и ProcessFile последовательно для каждого файла в архиве (подробности опишу далее)
- в конце разовый вызов CloseArchive

Теперь подробнее.
I. Когда пользователь заходит в архив, то первым делом TC должен получить список файлов. Для этого:
1. Вызывается функция
HANDLE __stdcall OpenArchive(tOpenArchiveData *ArchiveData);
параметр ArchiveData->ArcName содержит имя файла архива
ArchiveData->OpenMode установлен в PK_OM_LIST
Функция должна подготовить данные для последующего получения списка файлов и каталогов в архиве. Стратегии могут быть разные. Можно открыть архив, сразу составить этот список и закрыть архив. Можно открыть файл архива и настроить всё для последующего последовательного чтения. В любом случае, необходимо как-то запомнить всё сделанное (наиболее подходящим местом выглядит динамическая память) и вернуть в качестве результата вызова число типа HANDLE. Это может быть например указатель на созданную структуру или дескриптор открытого файла. Лично я практикую объектно-ориентированный подход, так что с архивом у меня связан объект соответствующего класса, созданный в хипе, а возвращаю я указатель на этот объект.
2. Далее TC получает от плагина список файлов в архиве. Для этого он начинает вызывать функцию
int __stdcall ReadHeader(HANDLE hArcData, tHeaderData *HeaderData);
С каждым вызовом (кроме последнего, см. далее) плагин возвращает информацию строго об одном файле.
здесь hArcData - это тот указатель, который был ранее получен фунцией OpenArchive
HeaderData - это структура, которую надо заполнить информацией о текущем файле из архива. Эту информацию получит TC. Все незаполненные поля надо обнулить.
Если ReadHeader возвращает информацию о файле (т.е. заполняет HeaderData), то сама функция должна вернуть 0.
Если файлов больше нет, то заполнять HeaderData не надо, а функция должна вернуть E_END_ARCHIVE. После этого функция больше не вызывается.
Внимание! Это всё означает, что функция ReadHeader будет вызвана число раз на 1 больше, чем есть в архиве файлов и каталогов.

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

Для каждого найденного файла, т.е. после каждого вызова ReadHeader, который вернул 0, происходит вызов функции
int __stdcall ProcessFile(HANDLE hArcData, int Operation, char *DestPath, char *DestName);
где Operation имеет значение PK_SKIP. Это означает, что делать здесь ничего не надо. Можно однако предположить ситуацию, что для перечисления файлов надо их фактически распаковать. А поскольку распаковка позже будет при следующих циклах происходить именно в этой функции, то вызов этой функции и для простого открытия архива выглядит логичным. Ещё раз однако, чаще всего при перечислении файлов здесь не надо делать ничего.

В документации в разделе о струтуре tOpenArchiveData написано:
If the file is opened with OpenMode==PK_OM_LIST, ProcessFile will never be called by Total Commander.
Т.е. утверждается, что ProcessFile не будет вызываться, если файл был открыт для составления списка файлов. Это неправда и противоречит написанному в том-же документе в разделе о функции ProcessFile. И эксперимент подтверждает, что ProcessFile вызывается в любом случае.

Ещё раз. ProcessFile вызывается ровно столько раз, сколько было найдено файлов и каталогов в архиве (т.е. столько раз, сколько вызов ReadHeader вернул 0).

3. После того как ReadHeader возвращает [/b]E_END_ARCHIVE[/b] TC вызывает функцию
int __stdcall CloseArchive (HANDLE hArcData);
Всё, что требуется от этой функции - почистить за собой созданные данные. Напомню, что у меня там был объект, и всё, что мне нужное здесь сделать это вызвать операцию delete (я пишу на C++). В принципе и эта функция может вернуть ошибку, но учитывая, что деструкторы исключений не вызывают =), у меня одним геморроем меньше.

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

II. Теперь пользователь выбрал некоторые файлы и каталоги, установил путь назначения и нажал "распаковать". Теперь TC запускает второй самостоятельный цикл работы плагина:
1. Вызывается функция OpenArchive
ArchiveData->OpenMode установлен в PK_OM_EXTRACT
параметр ArchiveData->ArcName как и раньше содержит имя файла архива и его надо открывать повторно.
Общая идея этой функции сохраняется, но теперь надо заготовить всё для последующей распаковки. Это может быть тот же набор действий, что и для получения списка файлов, а может потребовать и больше работы (для этого вероятно циклы и сделаны независимыми)

2. Далее TC вызывает последовательно ReadHeader[[b]b] и [b]ProcessFile для каждого из файлов и каталогов, полученных от плагина при открытии файла, и строго в том порядке, в котором он их получал.
Эта часть похожа на происходившее ранее при перечислении файлов. Есть однако и отличия. Самое первое заключается в том, что вызовов как ReadHeader так и ProcessFile будет ровно столько, сколько есть файлов и каталогов (поскольку TC уже имеет информацию об их количестве).

Теперь по порядку.
ReadHeader
Вызывается совершенно так-же, как и до этого при перечислении файлов. Казалось бы, у TC уже есть имена файлов из архива, мог бы и передать нам, дабы не извлекать их ещё раз. Но нет. Фактически, всю работу по созданию списка надо повторить заново. В этом цикле возникает дополнительная задача. Надо передать информацию о текущем файле в архиве последующему вызову ProcessFile, поскольку ему TC передаёт только путь назначения. Очевидно, что эту информацию (строка, итератор и пр. в этом роде) надо сохранить в структурах данных, связанных с тем самым hArcData. У меня это просто дополнительный член моего класса - строка с именем текущего файла.

ProcessFile
В качестве аргумента Operation получает значение
- PK_TEST - для тестирования или PK_EXTRACT - для распаковки для всех отмеченных пользователем файлов. Подчёркиваю - только файлов, не каталогов.
- PK_SKIP - для всех не отмеченных файлов и для всех каталогов. С файлами всё ясно, а с каталогами ситуация следующая. TC сам создаёт все каталоги, которые отмечены для распаковки, при этом даёт им текущее время создания. В принципе, ничто не мешает при распаковке не игнорировать каталоги, а менять их атрибуты и время на те, что указаны в архиве. Но надо помнить про ситуацию распаковки в уже существующую каталожную структуру. Существовал ли каталог до распаковки или был сделан только что? Проще не заморачиваться на этот счёт.
Полный путь для распаковки содержится в двух аргументах DestName и DestPath. В офф. документации замечательно написано:
Either DestName contains the full path and file name and DestPath is NULL, or DestName contains only the file name and DestPath the file path.
Т.е. либо
DestName = "c:\dir\file.txt"
DestPath = NULL

либо
DestName = "file.txt"
DestPath = "c:\dir\"

Как это понимать? Надо проверять по какому варианту сейчас взбрело в голову TC передать этот путь? У меня пока всегда срабатывало по первому варианту (DestName содержит полный путь включая имя, а DestPath = NULL).
А вот какой файл в архиве распаковывать, придётся узнавать самостоятельно. TC это несомненно знает, но молчит. Расчёт очевидно идёт на то, что обход файлов осуществляется строго в том же порядке, что и при получении списка файлов. Соответственно для решения этой задачи мы и запомнили текущий файл в предшествующем вызове ReadHeader.

3. CloseArchive как и раньше завершает всю цепочку действий. Чистим за собой все данные.

Всё! Все последующие распаковки происходят по такому же алгоритму.

З.Ы.: Что-то в процессе я разошёлся малость, и вышло даже маленькое руководство. Жаль, что под спойлер не спрятать.
Back to top
View user's profile Send private message
VadiMGP



Joined: 21 Mar 2007
Posts: 1625

Post (Separately) Posted: Sat Jun 27, 2009 00:38    Post subject: Reply with quote

Mowgli wrote:
Вероятно просто потому, что я сейчас делаю плагин для распаковки. Да и что изменится в плане вышеобсуждённого вопроса для других операций?
В том-то и дело , что ничего не изменится и для других операций. Но закавыка в том, что не бывает плагинов только для распаковки. Как минимум, ты должен поддерживать еще и просмотр. Так что твой вопрос именно о возможности параллельной распаковки вызвал у меня некоторое удивление. Ведь те же самые проблемы ты должен был засечь еще на этапе просмотра архива.
Quote:
- во-первых, никак нельзя описывать работу этих функций по-отдельности, поскольку они являются частью одного алгоритма.
Весьма странное утверждение. Поскольку кирпич является частью здания нельзя описывать свойства кирпича не описав свойств здания в целом?
Mowgli wrote:
Т.е. утверждается, что ProcessFile не будет вызываться, если файл был открыт для составления списка файлов. Это неправда и противоречит написанному в том-же документе в разделе о функции ProcessFile.
Это, конечно, неточность. Имеется в в иду, что ProcessFile не будет вызываться для операций с файлами, а только с PK_SKIP.
Quote:
Список файлов я составляю уже в функции OpenArchive,
Мне просто любопытно стало - а для каких архивов ты пишешь плагин? Ну и возник ряд других вопросов. Может ли количество файлов в твоём архиве достигать сотен тысяч или миллионов? Если да, то сколько памяти потребуется при вызове функции OpenArchive? Сколько времени ты будешь составлять этот список если архивный файл лежит на сетевом ресурсе?
Mowgli wrote:
Дальнейшая навигация внутри архива и даже повторные заходы в этот архив осуществляются без участия плагина.
Дальнейшая навигация внутри архива и повторные заходы в этот архив могут осуществляться без участия плагина.
Mowgli wrote:
Казалось бы, у TC уже есть имена файлов из архива, мог бы и передать нам, дабы не извлекать их ещё раз.
Нет. Архив мог за это время измениться и ТС предпочитает не рисковать и работать с обновленными данными.
Mowgli wrote:
но учитывая, что деструкторы исключений не вызывают
С чего бы это? Попробуй в деструкторе разделить что-то на 0.
Mowgli wrote:
Полный путь для распаковки содержится в двух аргументах DestName и DestPath. В офф. документации замечательно написано:
Either DestName contains the full path and file name and DestPath is NULL, or DestName contains only the file name and DestPath the file path.
...
Как это понимать? Надо проверять по какому варианту сейчас взбрело в голову TC передать этот путь?
Если хочется можно и проверять, но проще конкатенировать две строки и двигаться дальше.
Mowgli wrote:
А вот какой файл в архиве распаковывать, придётся узнавать самостоятельно. TC это несомненно знает, но молчит.
"Узнавать" - это сильное выражение в данном контексте. ТС передает тебе hArcData, который ассоциирован тобой же с текущим файлом. Так что эта фраза звучит примерно как "У меня есть адрес строки. Теперь мне самостоятельно придётся узнать что это за строка". Smile
Mowgli wrote:
Расчёт очевидно идёт на то, что обход файлов осуществляется строго в том же порядке, что и при получении списка файлов.
Это в некотором смысле тавтология. Обход файлов и получение списка это одно и то же.
Back to top
View user's profile Send private message
Mowgli



Joined: 25 Jun 2009
Posts: 11

Post (Separately) Posted: Sat Jun 27, 2009 04:11    Post subject: Reply with quote

Quote:
Весьма странное утверждение. Поскольку кирпич является частью здания нельзя описывать свойства кирпича не описав свойств здания в целом?
Аналогии=) Люблю аналогии. Вот ещё одна: как понять назначение колеса, если нет телеги?

Quote:
Это, конечно, неточность
Это высказывание носит весьма категоричный характер и поначалу меня весьма сбило с толку.

Quote:
Мне просто любопытно стало - а для каких архивов ты пишешь плагин? Ну и возник ряд других вопросов. Может ли количество файлов в твоём архиве достигать сотен тысяч или миллионов? Если да, то сколько памяти потребуется при вызове функции OpenArchive? Сколько времени ты будешь составлять этот список если архивный файл лежит на сетевом ресурсе?

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

Quote:
Дальнейшая навигация внутри архива и повторные заходы в этот архив могут осуществляться без участия плагина.

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

Quote:
Mowgli wrote:
но учитывая, что деструкторы исключений не вызывают
С чего бы это? Попробуй в деструкторе разделить что-то на 0.

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

Quote:
Если хочется можно и проверять, но проще конкатенировать две строки и двигаться дальше.

Если бы DestPath был пустой строкой, то да. А так он NULL. Всё равно проверять надо.

Quote:
"Узнавать" - это сильное выражение в данном контексте. ТС передает тебе hArcData, который ассоциирован тобой же с текущим файлом. Так что эта фраза звучит примерно как "У меня есть адрес строки. Теперь мне самостоятельно придётся узнать что это за строка". Smile

Для начала hArcData - это ещё не текущий файл в архиве. Из него ещё надо список файлов получить. Для этого возможно требуются немалые затраты, как было справедливо замечено выше. И это при том, что мы эту работу уже один раз проделали, и TC уже имеет этот список. Я конечно понимаю, что одного только имени может не хватить, чтобы найти этот файл в архиве и извлечь, но вот в моём случае этого как раз достаточно. Что стоило в случае повторного обхода для распаковки передавать мне обратно в структуре HeaderData при вызове ReadHeader ту информацию, что плагин ранее отдал TC?
Впрочем, вопрос не стоит обсуждения. Я то просто хотел понять как это работает. Если именно так, значит с этим и буду жить =)

Quote:
Quote:
Расчёт очевидно идёт на то, что обход файлов осуществляется строго в том же порядке, что и при получении списка файлов.
Это в некотором смысле тавтология. Обход файлов и получение списка это одно и то же.

Ещё раз. Я имел в виду два обхода. Первый был выполнен при построении списка файлов с опцией PK_OM_LIST. На основе этого цикла TC построил список файлов. Затем для распаковки был запущен второй цикл с опций PK_OM_EXTRACT. При этом TC очевидно полагается на то, что список файлов вторично построенный плагином и его, сохранённый ранее, будут в точности совпадать. Если они не будут совпадать, то начнутся большие проблемы. Ведь TC не передаёт при очередной итерации имя файлов для распаковки, только путь назначения. И если взаимный порядок файлов у TC и у плагина нарушается, то как плагин сможет узнать, что он распаковывает нужный файл?
Для меня сейчас важно понять, правильно ли я понимаю логику работы всей системы. Мне это надо, чтобы продолжить работу над плагином.
Back to top
View user's profile Send private message
VadiMGP



Joined: 21 Mar 2007
Posts: 1625

Post (Separately) Posted: Sat Jun 27, 2009 11:34    Post subject: Reply with quote

Mowgli wrote:
Люблю аналогии. Вот ещё одна: как понять назначение колеса, если нет телеги?
Я тоже люблю отвечать вопросом на вопрос. Но какой в этом смысл в данном случае? Smile
Mowgli wrote:
В каком случае TC захочет перечитать заново содержимое архива?
Наверняка это неизвестно. Я уже давно не писал архиваторные плагины. Насколько я помню, ТС держит в кэше не более двух последних архивов. Но это эмпирические данные, они могут меняться от версии к версии.
Mowgli wrote:
Если архив не менялся, то зачем ему это может потребоваться?
От греха подальше. Нет четких критериев для определения изменений в архиве. Размер и время - не показатель.
Quote:
И наконец, какова может быть роль плагина в навигации внутри архива?
Для навигации (после входа в архив и до выхода из него) плагин действительно не нужен.
Mowgli wrote:
Правильно написанные деструкторы исключений вызывать не должны. В противном случае они нарушают спецификацию языка C++.
Не спецификацию, а рекомендацию. Например, если класс не содержит конструктора по умолчанию, то нет никаких проблем с исключениями в деструкторе.
Mowgli wrote:
Если бы DestPath был пустой строкой, то да. А так он NULL. Всё равно проверять надо.
Я никогда не пользовался STL, так что тебе виднее. Я пользуюсь ATL или MFC. Там дополнительных проверок не требуется.
Mowgli wrote:
Для начала hArcData - это ещё не текущий файл в архиве.
А я и не утверждал , что hArcData - это текущий файл. Я утверждал, что он ассоциирован с текущим файлом. Причем тобой же, во время предшествующего вызова ReadHeader. Почему для получения собственной ассоциации из собственного итератора нужно строить список файлов да ещё с немалыми усилиями - мне непонятно.
Mowgli wrote:
При этом TC очевидно полагается на то, что список файлов вторично построенный плагином и его, сохранённый ранее, будут в точности совпадать. Если они не будут совпадать, то начнутся большие проблемы.
Нет, ТС ни на что не полагается и просто заново начинает делать перебор всех файлов. И когда твой плагин во время очередного вызова ReadHeader возвращает нужный файл, то последующий вызов ProcessFile будет с параметром PK_EXTRACT.
Back to top
View user's profile Send private message
Worros



Joined: 19 Jan 2007
Posts: 1123

Post (Separately) Posted: Sat Jun 27, 2009 21:18    Post subject: Reply with quote

Mowgli wrote:
В каком случае TC захочет перечитать заново содержимое архива?
Как минимум при нажатии Ctrl+R.
_________________
педофилия, каннибализм, бетономешалка !!!
Back to top
View user's profile Send private message
Mowgli



Joined: 25 Jun 2009
Posts: 11

Post (Separately) Posted: Sun Jul 05, 2009 23:47    Post subject: Reply with quote

Worros wrote:
Mowgli wrote:
В каком случае TC захочет перечитать заново содержимое архива?
Как минимум при нажатии Ctrl+R.
Похоже, что не так. По Ctrl+R просто выходит из архива наружу и больше не делает ничего.

VadiMGP wrote:
Я тоже люблю отвечать вопросом на вопрос. Но какой в этом смысл в данном случае? Smile
Это был не вопрос, а встречная аналогия в форме вопроса. Ну коли так не нравится, то просто отвечу на самый первый вопрос, который про кирпич. Да, в самом деле нельзя описать кирпич, не описав предвартельно здание. Во-первых, само понятие кирпича становится понятно, только если известно, что такое здание вообще. Во-вторых (и это подтвердит любой строитель), нельзя подобрать кирпич, не зная ничего о здании, которое будет из него построено.
Если с кирпичом это не вполне очевидно, то с элементом механизма это должно было стать более понятно. Функция же по определению является активным элементом (functio = исполнение) и в большей степени подобна части сложного механизма. Соответственно и её назначение как части системы без описания системы понять сложнее. Об этом и была аналогия.

Quote:
Не спецификацию, а рекомендацию.
Я процитирую это место из стандрата:
"If a destructor called during stack unwinding exits with an exception, terminate is called (15.5.1). So destructors should generally catch exceptions and not let them propagate out of the destructor."
Да, написано should. Можно и не следовать этому указанию. Примерно, как можно не следовать указанию "не влезай - убъёт" на высоковольтном щитке.

Quote:
Например, если класс не содержит конструктора по умолчанию, то нет никаких проблем с исключениями в деструкторе.
Откровенно не понял связи между конструктором по умолчанию и исключениями в деструкторе.

Quote:
Я никогда не пользовался STL, так что тебе виднее. Я пользуюсь ATL или MFC. Там дополнительных проверок не требуется.
Я давно не использовал MFC. Проверил, да действительно нулевой указатель интерпретируется как пустая строка. Убогое архитектурное решение.
Убогое, поскольку одному значению придаёт два разных смысла. Нулевой указатель имеет однозначную интерпретацию - память под объект не выделена. Является это логической ошибкой или ошибкой времени выполнения - в сущности не важно, поскольку в обоих случаях на это надо реагировать однозначно, т.е. генерируя ошибку. Пустая же строка - это на самом деле не отсутствие объекта, а объект со вполне конкретным значением. И при такой архитектуре невозможно отличить ситуацию ошибки от значения с пустой строкой. Ошибка маскируется, возможно проявляется где-то в другом месте. Это мизерное удобство, которое ведёт к потенциальнм и серьёзным проблемам.

Quote:
заново начинает делать перебор всех файлов. И когда твой плагин во время очередного вызова ReadHeader возвращает нужный файл, то последующий вызов ProcessFile будет с параметром PK_EXTRACT.
Вот информация, которой мне не хватало для понимания алгоритма. Спасибо! Значит, TC ищет файл, полученный от ReadHeader в ранее построенном списке файлов. Вот теперь всё понятно.

Я набросал псевдокод на C++ для циклов сканирования и распаковки, как я его вижу.

1. Открытие архива:

Code:
tOpenArchiveData arch_data = {};
arch_data.ArcName = archive_path; // полный путь до файла архива
arch_data.OpenMode = PK_OM_LIST; // режим получения списка файлов

HANFLE hArch = OpenArchive(&arch_data);
tHeaderData HeaderData;
while (0 == ReadHeader(hArch, &HeaderData))
{
   file_list.add(HeaderData.FileName); // добавляем очередной файл к списку файлов в архиве
   ProcessFile(hArch, PK_SKIP, 0, 0);
}
CloseArchive(hArch);
// здесь сохраняем полученный список файлов file_list и дескриптор архива hArch до следующего цикла.


2. Распаковка:
Юзер выделил некоторые файлы в списоке файлов. На основе этой информации TC составляет список выделенных файлов selected_file_list, который является подмножеством file_list.
Кроме того, есть ещё и путь назначения dest_dir.

Code:
tOpenArchiveData arch_data = {};
arch_data.ArcName = archive_path; // полный путь до файла архива
arch_data.OpenMode = PK_OM_EXTRACT; // режим распаковки

HANFLE hArch = OpenArchive(&arch_data);

tHeaderData HeaderData;
do
{
   err == ReadHeader(hArch, &HeaderData));
   if (err) break;
   if (selected_file_list.has(HeaderData.FileName))
   {
      selected_file_list.remove(HeaderData.FileName)
      ProcessFile(hArch, PK_EXTRACT, dest_dir, HeaderData.FileName);
      // или так
      // ProcessFile(hArch, PK_EXTRACT, 0, dest_dir + HeaderData.FileName);
   }
   else
   {
      ProcessFile(hArch, PK_SKIP, 0, 0);
   }
}
while (selected_file_list.not_empty());

CloseArchive(hArch);


В общем, плагин я написал. Прошу любить и жаловать.

S.T.A.L.K.E.R. db unpacker 0.0.1 beta
Back to top
View user's profile Send private message
VadiMGP



Joined: 21 Mar 2007
Posts: 1625

Post (Separately) Posted: Mon Jul 06, 2009 10:32    Post subject: Reply with quote

Mowgli wrote:
Да, в самом деле нельзя описать кирпич, не описав предвартельно здание.
Нда-а. Ну, делать нечего. У нас с тобой разные вселенные, построенные на разной аксиоматике. Не договоримся.

Особенно, когда так уверенно несут такую м-м-м... прелесть.
Mowgli wrote:
Нулевой указатель имеет однозначную интерпретацию - память под объект не выделена.
Абалдеть! Оказывается в той параллельной вселенной еще не обнаружили тот факт, что "объект" и "указатель на объект" это две разные вещи.

Mowgli wrote:
Да, написано should. Можно и не следовать этому указанию. Примерно, как можно не следовать указанию "не влезай - убъёт" на высоковольтном щитке.
Но шансы на контакт с внеземной цивилизацией сохранились. Тут совершенно верная аналогия. Если ты знаешь что находится за щитком, знаешь как оно работает и умеешь с ним обращаться - влезай сколько угодно. А если не знаешь, или не умеешь - держись подальше.

Интересно, откуда взялось высказывание, что если бы строители строили дома так же как программисты пишут программы, то первый же залетевший дятел разрушил бы цивилизацию.
Ведь для этого нет, ровным счетом, никаких оснований...
Back to top
View user's profile Send private message
Mowgli



Joined: 25 Jun 2009
Posts: 11

Post (Separately) Posted: Mon Jul 06, 2009 16:35    Post subject: Reply with quote

Прошу прощения, продолжать беседу дальше не могу. Плагин уже написан. Без информации отсюда сделать бы это не смог. Так что спасибо ещё раз.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Total Commander Forum Index -> Написание плагинов для Total Commander All times are GMT + 4 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group