Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Свитки
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
 
 12:50 Губернатор
 
 
Во Флориде и в Королевстве сейчас  12:50[Войти] | [Зарегистрироваться]

TUnRar без DLL

Дмитрий Мозулёв
дата публикации 28-11-2006 07:43

TUnRar без DLL

Здравствуйте. Перед вами моя первая статья. В ней рассматриваются сразу два немаловажных вопроса: "Как программно распаковывать *.rar архивы" и "Что сделать, чтобы не таскать *.dll-библиотеки за своим приложением". Обе темы заслуживают отдельного разговора. Начать предлагаю с первой.

Как программно распаковывать *.rar архивы

Давным-давно, в начале 90х, будучи студентом Челябинского Технического Университета (специальность ЭВМ), Евгений Рошал (будущий автор "WinRar" и "FAR") решил создать архиватор. Первые попытки успехом не увенчались, проект простаивал более года. Первую нормальную версию архиватора RAR 0.1 (предполагаю, аббревиатура "RAR" — это "Roshal ARchive") в марте 1993 года Евгений рискнул показать нескольким знакомым. Архиватор стремительно развивался и, уже осенью того же года, версия 1.3 (не без помощи Андрея Спасибожко) пошла в массы. Сегодня WinRar — самый популярный архиватор. О том, как программно распаковывать такие архивы в Delphi, нам и предстоит сегодня поговорить.


Евгений Рошал

Вспоминаю, как несколько лет назад был безумно рад, когда случайно в небезызвестных "Советах по Delphi от Валентина Озерова" наткнулся на раздел "Создаём собственный UnRar, используя unrar.dll". Целый день я мучился, прорабатывая приведённый листинг (потом кстати, где только не натыкался на листинги "один в один"; смотрите здесь) . Кончилось всё тем, что расплевавшись, устав комментировать "лишние" строки, расстроившись от бесконечных ошибок доступа к памяти, забросил проект на несколько лет…

Ну, к сути. Программно распаковать *.rar архивы можно двумя способами:

  1. вызвать WinRar.exe из командной строки с соответствующими ключами
  2. воспользоваться API функциями библиотеки "UnRar.dll"

Первый способ я описывать не буду (потому что не знаю), речь пойдёт о втором. Библиотеку "unrar.dll" можно найти в папке, в которую установлен WinRar или в архиве, который прилагается к статье. Для того чтобы приложение могло работать с этой библиотекой, dll должна располагаться либо в одной папке с exe, либо в системной папке Windows.

Напишем мы много. Сведётся, правда, всё к одной процедуре UnRarFile, первый аргумент которой — rar-файл, второй — результирующая директория [которую можно и не указывать]:

procedure UnRarFile(RarFileName : string; Directory : string = '');

Сразу оговорюсь: Unrar API сложнее, чем хотелось бы. Поэтому некоторые проверки на ошибки и описания различных режимов "разархивации" я опустил намеренно.

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

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Lines.Clear;
  // распаковать архив 'C:\Фото.rar'

  UnRarFile('C:\Фото.rar'); // распакуется в папку "C:\Фото\"
end;

Ну, что, готовы творить свои "UnRar — разархиваторы" ? Тогда, в путь!

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

// добавить в Memo строку S
procedure LOG(S : string);
begin
  Form1.Memo1.Lines.Add(S);
end;

//---------  используемые структуры  -------------------------------------------
type

  TRAROpenArchiveData = record
    ArcName    : PChar;
    OpenMode   : cardinal;
    OpenResult : cardinal;
    CmtBuf     : PChar;
    CmtBufSize : cardinal;
    CmtSize    : cardinal;
    CmtState   : cardinal;
  end;
//-----
  TRARHeaderData = record
    ArcName    : array[0..259] of char;
    FileName   : array[0..259] of char;
    Flags      : cardinal;
    PackSize   : cardinal;
    UnpSize    : cardinal;
    HostOS     : cardinal;
    FileCRC    : cardinal;
    FileTime   : cardinal;
    UnpVer     : cardinal;
    Method     : cardinal;
    FileAttr   : cardinal;
    CmtBuf     : PChar;
    CmtBufSize : cardinal;
    CmtSize    : cardinal;
    CmtState   : cardinal;
  end;
//-----
  TUnRarCallBack   = function(msg: Cardinal; UserData, P1, P2: integer): integer; stdcall;

//------------  API  функции  --------------------------------------------------
function RAROpenArchive(var ArchiveData : TRAROpenArchiveData): THandle; stdcall;
external 'unrar.dll' name 'RAROpenArchive'; 

function RARCloseArchive(hArcData : THandle): Integer; stdcall;
external 'unrar.dll' name 'RARCloseArchive'; 

function RARReadHeader(hArcData : THandle; out HeaderData : TRARHeaderData): Integer; stdcall;
external 'unrar.dll' name 'RARReadHeader'; 

function RARProcessFile(hArcData : THandle; Operation : Integer; DestPath : pchar;
DestName : pchar = nil): Integer; stdcall;
external 'unrar.dll' name 'RARProcessFile';

procedure RARSetCallback (hArcData: THandle; Callback: TUnRarCallback; UserData: longint); stdcall;
external 'unrar.dll' name 'RARSetCallback';

//-------------  некоторые константы  ------------------------------------------
const 
ERAR_END_ARCHIVE = 10; 
RAR_OM_EXTRACT   =  1;
RAR_EXTRACT      =  2;
RAR_SUCCESS      =  0;
UCM_PROCESSDATA  =  1;

//-------------  использемые переменные  ---------------------------------------
var
  RARHeaderData : TRARHeaderData;
  ArcStruct: TRAROpenArchiveData;
  CmtBuffer : array[0..1023] of char;

//------------------------------------------------------------------------------
function UnRarCallBack(msg: Cardinal; UserData, P1, P2: integer): integer; stdcall;
begin
  Result := 0;
  if msg = UCM_PROCESSDATA then LOG('Распаковал ' + IntToStr(P2) + ' байт');
end;
//------------------------------------------------------------------------------
function OpenRARArchive(FileName : string) : THandle;
begin
  ZeroMemory(@ArcStruct, sizeof(ArcStruct));

  ArcStruct.OpenMode := RAR_OM_EXTRACT;
  ArcStruct.ArcName  := pchar(FileName);
  ArcStruct.CmtBuf   := CmtBuffer;
  ArcStruct.CmtBufSize := sizeof(CmtBuffer);

  Result := RAROpenArchive(ArcStruct);
//------------------------------------
  { ПОТОМ можно прочитать комментарии к архиву FileName:
    var Comments : string;
    SetString(Comments, ArcStruct.CmtBuf, ArcStruct.CmtSize);

    P.S. Следует всё же делать так:
         ArcStruct.CmtBufSize := 1024*64; // 64 Килобайта
         GetMem(ArcStruct.CmtBuf, ArcStruct.CmtBufSize);
         ...
         SetString(Comments, ArcStruct.CmtBuf, ArcStruct.CmtSize);
         FreeMem(ArcStruct.CmtBuf); }
//------------------------------------
end;
//------------------------------------------------------------------------------
procedure UnRarFile(RarFileName : string; Directory : string = '');
var
   hRAR : THandle;
   hReadHeader : integer;
   hProcessHeader : integer;
begin
   UniqueString(RarFileName);
   RarFileName := ExpandFileName(RarFileName);
   UniqueString(Directory);
   if length(Directory) = 0 then
      Directory := ChangeFileExt(RarFileName, '')
        else Directory := ExpandFileName(Directory);

   LOG('Распаковываю архив "' + RarFileName + '" в папку "' + Directory + '"...');

   // "корректирую" имя папки для UnRar.dll
   CharToOem(pchar(Directory), pchar(Directory));
   // открываю архив, получаю дескриптор
   hRAR := OpenRARArchive(RarFileName);
   // устанавливаю Callback-функцию
   RARSetCallback(hRar, UnRarCallBack, 0);

// сам процесс разархивации -->
   hReadHeader := 0;
   hProcessHeader := 0;
REPEAT
   hReadHeader := RARReadHeader(hRar, RARHeaderData);
   if hReadHeader = ERAR_END_ARCHIVE
      then Break;
   // <-- RARHeaderData содержит информацию о текущем распакуемом файле

   OemToChar(RARHeaderData.FileName, RARHeaderData.FileName);

   LOG('Распаковываю файл "' + string(RARHeaderData.FileName) +
       '"... ' + IntToStr(RARHeaderData.UnpSize) + ' байт.');

   if hReadHeader = RAR_SUCCESS then
        hProcessHeader := RARProcessFile(hRar, RAR_EXTRACT, PChar(Directory), nil);

UNTIL (hProcessHeader <> RAR_SUCCESS);
// <-- сам процесс разархивации


   LOG('Всё распаковано, закрываю архив.');
   RARCloseArchive(hRAR)
end;
//------------------------------------------------------------------------------
procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Lines.Clear;
  UnRarFile('C:\фото.rar');
end;
//------------------------------------------------------------------------------
end.

Прежде, чем начать разбираться с кодом, давайте лучше попрактикуемся! Перенесите вышеуказанный программный код в свой проект, растяните Memo пошире, Memo.ScrollBars задайте ssBoth, в обработчике TForm1.Button1Click укажите реально существующий rar-архив. Запускайте приложение, жмите на кнопку. У меня получилось что то наподобие:

Распаковываю архив "C:\фото.rar" в папку "C:\фото"...
Распаковываю файл "МОИ_ФОТКИ"... 0 байт.
Распаковываю файл "МОИ_ФОТКИ\Мы С Любой"... 0 байт.
Распаковываю файл "МОИ_ФОТКИ\Фотки Серёги"... 0 байт.
Распаковываю файл "МОИ_ФОТКИ\Мы С Любой\DSCN1281.JPG"... 307150 байт.
Распаковал 307150 байт
Распаковываю файл "МОИ_ФОТКИ\Мы С Любой\DSCN1282.JPG"... 262913 байт.
Распаковал 262913 байт
Распаковываю файл "МОИ_ФОТКИ\Мы С Любой\Thumbs.db"... 328192 байт.
Распаковал 328192 байт
Распаковываю файл "МОИ_ФОТКИ\Фотки Серёги\PICT0391.JPG"... 263412 байт.
Распаковал 263412 байт
Распаковываю файл "МОИ_ФОТКИ\Фотки Серёги\PICT0392.JPG"... 274698 байт.
Распаковал 274698 байт
Распаковываю файл "МОИ_ФОТКИ\Фотки Серёги\Thumbs.db"... 119296 байт.
Распаковал 119296 байт
Распаковываю файл "МОИ_ФОТКИ\Фотки Серёги\Ария - Возьми мое сердце.mp3"... 5066024 байт.
Распаковал 4194045 байт
Распаковал 259 байт
Распаковал 871720 байт
Всё распаковано, закрываю архив.

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

Следующие 3 строки показывают, что при разархивации, если в архиве есть поддиректории, сначала создаётся дерево каталогов. Разархивация самих файлов начинается только с пятой строки. Сразу обратите внимание на последний разархивируемый файл (который неизвестно как оказался в моём архиве фотографий) — mp3-трек группы "Кипелов". Исходный mp3 файл занимает около 5мб (5066024 байт), однако разархивируется он в 3 этапа. Проверяйте: 5066024 = 4194045 + 259 + 871720.

Вопрос: "Почему некоторые файлы разархивируются всего в один этап?"

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

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

Алгоритм разархивации:

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

Теперь можно смотреть реализацию процедуры UnRarFile. Надеюсь, многое стало понятно.

Заголовки (TRARHeaderData)

Перед тем, как разархивировать какой-то файл, мы получаем его заголовок. Заголовок содержит множество параметров: имя разархивируемого файла (FileName), размер в архиве (PackSize), реальный размер (UnpSize), атрибуты файла (FileAttr; по ним, кстати, можно узнать, директория это или нет), ряд других параметров. Если вы желаете разархивировать не весь архив, а только его часть (только*.exe-файлы, например) и на очереди ненужный файл, то просто не вызывайте функцию RARProcessFile.

Кстати говоря, если нужно, поддерживается разархивация "в другой файл". Для этого в последнем аргументе функции RARProcessFile указывайте не nil, а новое имя файла. Не забывайте, что в UnRar строки в OEM кодировке.

Callback-функция

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

Сообщение (msg) может принимать не только константу UCM_PROCESSDATA, но и UCM_NEEDPASSWORD (когда архив запаролен) и UCM_CHANGEVOLUME (не знаю, как используется);

Тем не менее, самым главным остаётся UCM_PROCESSDATA. Когда вы вызываете процедуру RARProcessFile, файл может разархивироваться по частям. При каждой разархивации такой части, вызывается Callback функция с сообщением UCM_PROCESSDATA; P2 — размер разархивируемой части.

Реализация OnProcess()

Разархивируя файл в том же WinRAR, мы видим 2 полосы состояния: верхняя (какая часть архива уже распакована, какая ещё осталась) и нижняя (всё то же самое относительно текущего файла). Как реализовать нижнюю, думаю, предполагаете: при чтении заголовка нужно узнать, сколько занимает распакованный файл (сохранить в переменную Total, например) и занулить какую-то целочисленную переменную (Cur, например). В Callback функции, при получении сообщения UCM_PROCESSDATA, к Cur прибавляете локальный параметр P2.

Процент = round(Cur/Total*100).

Рассчитать процент распаковки всего архива оказывается сложнее. Даже с учётом того, что размер каждого файла находится элементарно (UnpSize), в структуре TRAROpenArchiveData нет ни намёка на что-либо типа GlobalUnPackSize. Ну и как быть?

В любом случае, GlobalUnPackSize найти всё же надо. Делается это примерно так (сильно упрощено):

GlobalUnPackSize := 0;
hRAR := OpenRARArchive(RarFileName);
RARSetCallback(hRar, UnRarCallBack, 0);
   
While RARReadHeader(hRar, RARHeaderData) <> ERAR_END_ARCHIVE do
Inc(GlobalUnPackSize, RARHeaderData.UnpSize);
RARCloseArchive(hRAR);   
// <-GlobalUnPackSize содержит сумму размеров
Процент = round(CurSumm/GlobalUnPackSize*100).

А при чём здесь TUnRar ?

Да, согласен, ни о какой реализации класса или уж тем более компонента TUnRar, речи не шло. Тем не менее, надеюсь, что всё же найдётся человек, который возьмётся за реализацию нормального компонента, не требующего dll, работающего корректно и чтобы интерфейс дружелюбный был.

Единственное, что мешает перенесению текущей реализации в ООП, это — функция UnRarCallBack. Загвоздка в том, что если объявить такую процедуру в public секции класса, то компилятор не позволит задать её в качестве параметра функции RARSetCallback(). И дело тут не в компиляторе; дело в соглашениях о вызове на уровне ассемблера.

К счастью, обходится это вещь просто. Последний аргумент функции RARSetCallback() — UserData: longint, занимает четыре байта, в него можно поместить ссылку на компонент…

Объясняю. Допустим, у нас есть класс TUnRar. Создадим в нём функцию TUnRar.RARCallBack(msg, P1, P2 : integer) : integer, которая в конечном счёте и будет принимать все сообщения. Допустим, будет так же процедура TUnRar.UnRarFile(), в которой вызов RARSetCallback() чуть-чуть изменим:

RARSetCallback(hRar, UnRarCallBack, longint(self)); // UserData хранит ссылку на компонент

Ну а UnRarCallBack будет выглядеть так:

function UnRarCallBack(msg: Cardinal; UserData, P1, P2: integer): integer; stdcall;
begin
  Result := TUnRar(UserData).RARCallBack(msg, P1, P2);
end;

Правда, здорово?

О существующих UnRar

Как это ни странно, пару реализаций таких компонентов я видел. Во-первых, это закрытый компонент в пакете ZipTV. Второй — немецкий компонент (открытый!) DFUnRar (качайте здесь). Оба не совсем удобны, русский не поддерживают, требуют unrar.dll.

Если сложить исходники DFUnRar, одного ленивого энтузиаста и эту статью, то можно сделать неплохой компонент. Ну а у меня, к сожалению, времени нет.

Ладно! Теперь избавляемся от этой unrar.dll !!!

Прощай DLL, или как интегрировать DLL в приложения на Delphi.

Для меня, честно говоря, до сих пор не понятно, почему же многие программисты стремятся избавиться от наличия используемых dll в своей директории. Я скажу больше, я — один из таких программистов :).

Кому-то, наверное, хочется получить некую автономность своего приложения (если приложение не найдёт требуемую dll, то корректно оно работать, мягко говоря, не будет). Кто-то, наверное, хочет скрыть использование платных (или бесплатных) Dll-библиотек (это и всевозможные графические, и скриптовые, и звуковые [BASS, MikMod, FMOD, …], и физические [Newton, ODE, Tokamak, …] движки, и коммерческие dll-библиотеки, и … ). Кто-то, наверное, имеет уйму кода на Си++ и хочет использовать его в Delphi; у каждого свои причины. Вообще-то, использовать Си-шный код можно и не создавая dll. Но это тема уже отдельной статьи… (ладно, пользуясь случаем, напишу попозже пару слов).

Мне известны всего три способа интеграции DLL в своё приложение:

  1. Включить DLL в файл ресурсов, файл ресурсов прилинковать к своему приложению. При загрузке, сохранить DLL в отдельный (лучше в папке "Temp") файл; динамически загрузить все функции из полученной DLL.
  2. Воспользоваться утилитой Dll2Lib, программировать на VisualC++.
  3. Воспользоваться утилитами DLLTools.

Первый способ, надеюсь, понятен всем. Второй способ я поясню. Существует довольно известная утилита DLL2Lib. Она, как можно догадаться, конвертирует DLL-библиотеки в статические *.lib-библиотеки. Полученную *.lib можно свободно использовать в приложениях на Visual C++. Builder C++ тоже использует *.lib-библиотеки, но попытка линковки такой библиотеки из-за несовместимости форматов (о них поговорим в конце) закончится ошибкой компилятора.

Поговорим теперь о последнем способе. Совсем недавно Vga дал мне ссылку, за что ему ОГРОМНОЕ СПАСИБО. Скачивать там ни чего не надо, к статье уже прилагается существенно изменённый архив DLLTools.zip . Именно его вы должны скачать для дальнейшей работы.

Архив содержит всего 7 файлов (по делу только 3). Первое, что нужно сделать, это скопировать в какую-нибудь глобальную директорию (в $Delphi\Lib\ , например) модуль "DLLLoader.pas". Это основной модуль, его автором является Benjamin Rosseaux (www.0ok.de). Несмотря на то, что этот и все остальные модули проекта я, как уже говорил, существенно изменил, истинным автором остаётся Benjamin Rosseaux.

Второй по важности является утилита Dll2Pas (она вообще полностью переписана). Именно она преобразует DLL в автономный *.pas файл-заготовку. Для того чтобы ей воспользоваться, скопируйте все конвертируемые DDL-библиотеки в директорию DLLTools и запустите утилиту. Если в директории находится только одна "UnRar.dll", то утилита создаст только один файл "UnRarLib.pas". Открыв этот модуль в Delphi, вы увидите, что он "поделён" на несколько блоков:

  1. БЛОК КОНСТАНТ и ТИПОВ
  2. БЛОК ФУНКЦИЙ и ПРОЦЕДУР
  3. БЛОК ВНУТРЕННИХ ФУНКЦИЙ и ПРОЦЕДУР
  4. ИНИЦИАЛИЗАЦИЯ ВСЕХ ФУНКЦИЙ и ПРОЦЕДУР
  5. БЛОК ДЕИНИЦИАЛИЗАЦИИ (если нужно)

Давайте заполним некоторые из них. В первый блок переместите описания всех типов (TRAROpenArchiveData, TRARHeaderData, TUnRarCallBack) и констант (ERAR_END_ARCHIVE, RAR_OM_EXTRACT, RAR_EXTRACT, RAR_SUCCESS, UCM_PROCESSDATA), относящихся к библиотеке UnRar.

Второй блок пока пропустим, в третий перенесём вспомогательную функцию OpenRARArchive(). Все переменные (RARHeaderData, ArcStruct, CmtBuffer) переместите в секцию interface (т.е. выше секции implimentation).

Пятый блок нам вообще не нужен (удалять его не обязательно). Осталось заполнить второй и четвёртый. Если свою DLL вы загружаете динамически, то вам не составит огромных усилий заполнить эти блоки. Если вы загружаете её статически (как в нашем случае), то на помощь приходит утилита Static2DynDLL. Пользоваться ей так:

  1. В файл "PROCS.txt" скопируете свои статические функции. Комментарии, если есть, удалять не обязательно. Описание всей функции должно быть во всю строку:
  2. function RARCloseArchive(hArcData : THandle): Integer; stdcall; external 'unrar.dll' name 'RARCloseArchive';
    . . .
    

  3. Запускаете утилиту Static2DynDLL
  4. Содержимое файла "pointers.txt" копируете во второй блок:
  5. RAROpenArchive: function(var ArchiveData : TRAROpenArchiveData): THandle; stdcall; 
    RARCloseArchive: function(hArcData : THandle): Integer; stdcall; 
    RARReadHeader: function(hArcData : THandle; out HeaderData : TRARHeaderData): Integer; stdcall; 
    RARProcessFile: function(hArcData : THandle; Operation : Integer; DestPath : pchar; DestName : pchar = nil): Integer; stdcall; 
    RARSetCallback: procedure (hArcData: THandle; Callback: TUnRarCallback; UserData: longint); stdcall;
    

  6. Содержимое файла "load" копируете в четвёртый блок:
  7. RAROpenArchive := GetProcedure('RAROpenArchive');
    RARCloseArchive := GetProcedure('RARCloseArchive');
    RARReadHeader := GetProcedure('RARReadHeader');
    RARProcessFile := GetProcedure('RARProcessFile');
    RARSetCallback := GetProcedure('RARSetCallback');
    

Вот и всё! Теперь вы — обладатели статической библиотеки "UnRar" для Delphi!!! Запускайте своё тестовое приложение и наслаждайтесь жизнью. На всякий пожарный, если кого-то заинтересует, каким образом на самом деле происходит загрузка DLL, читайте исходники DLLLoader.pas и статью Загрузчик PE-файлов.

Некоторые вопросы-ответы

Вопрос: Почему полученный модуль прибавляет к exe в 2 раза меньше, чем исходная DLL? Не потому ли, что удаляет из DLL ненужные куски кода?

Ответ: Нет. На самом деле используется inflate-сжатие. Такой подход экономит не только размер exe, но и занимаемое место в памяти.

Вопрос: Почему полученный модуль в 2 раза больше, чем исходная DLL?

Ответ: Для того чтобы "вписать" данные внутрь *.pas-файла, используется массив байт. На описание 1байта данных уходит 4байта (символа) текста. Т.к. исходный файл сжат примерно в 2 раза, а на запись каждого байта уходит 4байта, то: результат = размер(DLL)/2*4 = 2* размер(DLL)… Данные вряд ли будут записываться иначе!

Вопрос: В процессе работы моего приложения происходит ошибка доступа к памяти по адресу 0. Как лечить?

Ответ: Либо какой-то из функций нет в DLL, либо статическая функция была неправильно конвертирована утилитой Static2DynDLL. К примеру, если статическая функция выглядит так:

function MessageBox(…): Integer; stdcall; external user32 name 'MessageBoxA';

то утилита преобразует в: MessageBox := GetProcedure('MessageBox'); Все вопросы к Benjamin Rosseaux!

Вопрос: В моей DLL "зашиты" ресурсы. Как их оттуда можно загрузить?

Ответ: Понятия не имею. Можете изучить исходники и указанную выше статью, чтобы добавить такую возможность в TDLLLoader (если такое возможно).

Вопрос: какие изменения внёс ты?

Ответ:

  1. сжатие
  2. разделил полученный файл на блоки-комментарии
  3. изменил конструктор и описание класса TDLLLoader
  4. функция GetProcedure('') перестала быть чувствительна к регистру аргумента
  5. Dll2Pas заработала на порядок быстрее и сразу со всеми Dll в директории
  6. изменены имена файлов на входе и выходе утилиты Static2DynDLL

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

Приложение (как использовать код Си++ в Delphi)

Да, чуть не забыл, что обещал рассказать об этом. Ладно. Самый простой и надёжный способ — превратить С++ код в DLL, которую без проблем можно использовать в своём приложении.

Во-вторых можно постараться откомпилировать код в одном из C++ компиляторов формата OMF, а полученные *.obj файлы прилинковать к своему приложению директивой {$L …}. "Что за формат такой — OMF" — спросят некоторые. Дело в том, что существуют два несовместимых формата *.obj-файлов: OMF и COFF. Первый используется в Delphi, C++Builder, Watcom C++, Intel C++ Compiler, …, второй используется в Visual C++ и некоторых других компиляторах.

Можно, правда, воспользоваться утилитой Coff2Omf. Для того чтобы ей воспользоваться, создайте файл, например, "Execute.bat" следующего содержания:

COFF2OMF.EXE MyObjFile.obj
pause

Затем запустите этот файл на выполнение, должен получиться файл формата OMF.

Но, если всё же есть возможность откомпилировать его в OMF-компиляторе, то откомпилируйте в нём. Вот список бесплатных Си++ компиляторов. Можно скачать бесплатный Борландовский C++ компилятор.

Скачали? Теперь ради интереса попробуйте взять какой-нибудь C++ модуль, откомпилировать его и, если нужно, преобразовать его в OMF. Теперь попробуйте прилинковать его к Delphi… Ошибка? Я так и думал! Вот почти дословно то, что по данному поводу пишет Kvant на форуме сайта RSDN:

Delphi понимает OMF объектные файлы, да и то с некоторыми ограничениями. Ограничений, в основном, 3 штуки:

  1. Вызовы функций через импорт в Delphi сводятся к формату "call [jmp адрес]". Если в объектнике указан просто "call адрес", компилятор Delphi выдаст ошибку.
  2. Имена импортируемых функций не должны содержать декорации (типа _imp__имя@N), за исключением борландовского формата.
  3. Фиксапы (записи FIXUPP) должны быть исключительно 5-байтными.

Есть ещё ограничения на имена секций, TLS и т.д. Тем не менее, в большинстве случаев достаточно иметь в виду лишь те 3 вышеописанных ограничения, чтобы получить работоспособный OMF-объектник без использования сторонних утилит.

Рекомендую взглянуть на документацию и исходники утилиты OMF2D by EliCZ.

Вот теперь точно Всё!

P.S. меня по данной теме спрашивать ничего не надо, т.к. я не прилинковал к Delphi-приложению ещё ни одного объектника, написанного на Си; вся информация получена из Интернета и со слов Vga.



К материалу прилагаются файлы:


Смотрите также материалы по темам:
[Использование и создание DLL] [Архивация (алгоритмы сжатия)]

 Обсуждение материала [ 02-07-2008 05:22 ] 39 сообщений
  
Время на сайте: GMT минус 5 часов

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

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования