TUnRar без DLL, Різне, Програмування, статті

Автор: Дмитро Мозулев, королівство Delphi


У статті розглядаються відразу два важливих питання: “Як програмно розпаковувати *. Rar архіви” і “Що зробити, щоб не тягати *. Dll-бібліотеки за своїм додатком”. Обидві теми заслуговують окремого розмови. Почати пропоную з першою.


Давним-давно, на початку 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 + 871 720.


Питання: “Чому деякі файли розархівуйте всього в один етап?”


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


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


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










відкрити файл-архів, отримати дескриптор


цикл:


прочитати заголовок (визначаємо, який файл разархивируем)


розархівувати файл


якщо не досягли кінця архіву, то знову прочитати заголовок


закрити файл-архів


Тепер можна дивитися реалізацію процедури 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). Перше, що потрібно зробити, це скопіювати в яку-небудь глобальну директорію (в $ DelphiLib, наприклад) модуль “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” скопіюєте свої статичні функції. Коментарі, якщо є, видаляти не обов’язково. Опис всієї функції повинно бути у всю рядок:










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


. . .


2. Запускаєте утиліту Static2DynDLL


3. Вміст файлу “pointers.txt” копіюєте у другій блок:










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;


4. Вміст файлу “load” копіюєте в четвертий блок:










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.


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

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*