• Уважаемые гости и новички, приветствуем Вас на нашем форуме
    Здесь вы можете найти ответы практически на все свои вопросы о серии игр «Готика» (в том числе различных модах на нее), «Ведьмак», «Ризен», «Древние свитки», «Эра дракона» и о многих других играх. Можете также узнать свежие новости о разработке новых проектов, восхититься творчеством наших форумчан, либо самим показать, что вы умеете. Ну и наконец, можете обсудить общие увлечения или просто весело пообщаться с посетителями «Таверны».

    Чтобы получить возможность писать на форуме, оставьте сообщение в этой теме.
    Удачи!
  • Друзья, доброго времени суток! Спешите принять участие в конкурсе "Таинственные миры" 2024!
    Ждем именно вас!

    Ссылка на конкурсную тему - тык

Создаем инсталлятор...

siryksv

Участник форума
Регистрация
5 Окт 2009
Сообщения
567
Благодарности
1
Баллы
225
Хотел бы рассказать о построении полноценных инсталляторов с помощью подручных сред программирования. Обычно задача создания инсталлера сводится к использованию различных программ (типа Nsis, Inno и т.д.), в которых, как правило, есть свой встроенный скриптовый язык. Но: 1) специфические скрипты изучать нет особого желания; 2) функциональность инсталляторов все же несколько ограниченна в плане возможностей по управлению средствами ОС, хотя при распаковке очень большого количества файлов использование скриптовых систем может быть более выгодным.

Я расскажу об одном способе, который основан на использовании ресурсов, "подшиваемых" к исполняемому файлу. Хотя этот способ и известен в среде программистов, однако далеко не все им владеют.
Что такое ресурс? В том смысле, в котором это слово нам понадобится, ресурс – это совокупность данных, которые непосредственно не относятся к исполняемому коду, но включаются в файл и несут вспомогательные функции. Для ресурсов выделяется отдельная секция в исполняемом файле. Изображения, строки, звуки и анимации, пиктограммы – это все примеры ресурсов. Сведения о файле (имя программы, версия, разработчики, авторские права) – это тоже ресурсы (строкового типа). Для просмотра ресурсов исполнительных файлов можно воспользоваться программами типа ResHacker.

От теории переходим теперь к практике. Я покажу пример написания инсталлера в среде Borland Delphi. Суть данного способа – "засунуть" распаковываемые файлы в файл ресурсов (RES-файл), который затем подключается к программе на стадии компиляции в среде Delphi. В примере, описанном ниже, распаковывается пять JPEG-картинок (index1.jpeg, ..., index5.jpeg) в каталог, который выбирает пользователь. Процесс создания представлен совокупностью шагов.

Шаг 1. Надо создать файл (пусть это будет Install.rc), в котором специальным образом перечислены распаковываемые файлы. Файл этот выглядит следующим образом:
Код:
Index1 EXEFILE Index1.jpeg
Index2 EXEFILE Index2.jpeg
Index3 EXEFILE Index3.jpeg
Index4 EXEFILE Index4.jpeg
Index5 EXEFILE Index5.jpeg
Тут первый идентификатор обозначает имя (псевдоним) для ресурса, под которым ресурс будет виден программе. Второй параметр – тип ресурса, у нас должно быть EXEFILE. Третий параметр обозначает имя файла.

Шаг 2. Созданный на предыдущем этапе файл Install.rc нужно теперь передать в качестве единственного параметра компилятору ресурсов brcc32.exe, который находится в папке <каталог_с_Delphi>\Bin – найти его не сложно. Желательно скопировать в одну папку сам brcc32.exe, скрипт Install.rc и файлы, которые будем паковать (у нас это JPEG-картинки). Затем запускаем в консоли команду
Код:
brcc32.exe Install.rc
. На выходе получается файл Install.res. Это и есть нужный нам файл. Копируем его в папку с проектом инсталлятора на Delphi.

Шаг 3. Работаем с проектом на Delphi. Полученный ресурсный файл Install.res надо подключить к нашей программе, и делается это путем вставки директивы {$R Install.res} в главный файл проекта – у нас в примере это файл Project1.dpr.
Теперь пишем процедуру для извлечения исходных файлов из ресурсов программы. Возможная реализация такой процедуры может иметь следующий вид:
Код:
procedure ExtractRes(ResType,ResName,ResNewName:String);
var Res:TResourceStream;
begin
 Res:=TResourceStream.Create(hInstance,Resname,Pchar(ResType));
 Res.SavetoFile(ResNewName);
 Res.Free;
end;
Формальные параметры этой процедуры имеют следующий смысл:
ResType – тип извлекаемого ресурса, для нас это EXEFILE;
ResName – идентификатор ресурса в программе, см. описание структуры файла Install.rc;
ResNewName – полный путь к файлу, в который и будет переписан этот ресурс.

Непосредственно для работы с ресурсом используется встроенный класс системы Delphi – TResourceStream, который и обеспечивает всю "грязную" и рутинную работу с ресурсом. В VC++, кстати, такой роскоши нет, и для работы с ресурсами пришлось бы использовать WinApi-функции. Извлечение файлов с помощью процедуры ExtractRes осуществляется примерно так:
Код:
procedure Extract;
begin
 with Form1 do  begin
   ExtractRes('EXEFILE','Index1',PathEdit.Text+'\Index1.jpeg');
   ExtractRes('EXEFILE','Index2',PathEdit.Text+'\Index2.jpeg');
   ExtractRes('EXEFILE','Index3',PathEdit.Text+'\Index3.jpeg');
   ExtractRes('EXEFILE','Index4',PathEdit.Text+'\Index4.jpeg');
   ExtractRes('EXEFILE','Index5',PathEdit.Text+'\Index5.jpeg');
  end;
end;
Процедура Extract извлекает все файлы в папку пользователя. Подробности по визуальному оформлению проекта, естественно, здесь опускаются, так как, скорее всего, это дело личного вкуса.

Ниже в RAR-архиве приложен описанный проект в полностью рабочем состоянии. Пишите в случае возникновения вопросов. Также даю скриншот того, что получилось в результате (Screen.JPG).
(C) siryksv, 2009-2010.
 

Вложения

  • Installer.rar
    679,2 KB · Просмотры: 568
  • Screen.JPG
    Screen.JPG
    24,6 KB · Просмотры: 637

siryksv

Участник форума
Регистрация
5 Окт 2009
Сообщения
567
Благодарности
1
Баллы
225
Хотелось бы узнать, нужно ли развивать эту тему? Я мог бы описать создание инсталлера, используя те же ресурсы, но на VC++ (только уже не используя классы Delphi, а на чистом WinApi 8)). Также можно бы рассмотреть вопросы работы с реестром в системе Delphi (а также и через WinApi), создания деинсталлирующей программы, создания ярлыков и т.д. Что скажете? ::)
 

Wallerstein

Участник форума
Регистрация
26 Апр 2009
Сообщения
965
Благодарности
23
Баллы
195
Интересно бы узнать, а это может чем-нибудь помочь в модостроении или нет?
 

siryksv

Участник форума
Регистрация
5 Окт 2009
Сообщения
567
Благодарности
1
Баллы
225
Wallerstein
Моды инсталлировать...:D А если по-сути, то ведь раздел "Программирование" к теме модостроения имеет довольно таки отдаленное отношение... В шапке темы написано:
Программирование Обсуждение вопросов, связанных с программированием на различных языках.
:-*
 

GeorG

Участник форума
Регистрация
27 Авг 2008
Сообщения
3.447
Благодарности
11
Баллы
295
Интересно бы узнать, а это может чем-нибудь помочь в модостроении или нет?
Может и помочь... если ты выше указанным способом, все файлы от ресурсов игры, запихаешь в ресурсы одного exe файла... Можно таким способом сделать инсталлятор... ихмо, только это очень тяжело :)

siryksv
Прикольная, темка... я лет 5 назад, эту инфу по крупицам собирал :) Млин... прям ностальгия :)
 

siryksv

Участник форума
Регистрация
5 Окт 2009
Сообщения
567
Благодарности
1
Баллы
225
Продолжу тему создания инсталляторов. Может быть, кому-то и понадобится...

Итак, создание инсталлятора на Visual C++.
Для написания примера я использовал "классический" VisualC++ 6, хотя технология создания данной программы под другими версиями компилятора (например, 2003-й) особо ничем не будет отличаться. ::)

Шаг 1. Создадим пустой MFC-проект (File->New->MFC AppWizard (Exe)) с названием VCInstall. Так как нам, в принципе, понадобится одно окно, то в процессе работы мастера выбираем тип приложения Dialog based. Все остальные настройки можно оставить по умолчанию. Оформление окна инсталлятора я оставляю на совесть пользователя, то что у меня самого получилось, видно на скрине в приложениях. Однако, самый главный компонент нашей формы – это кнопка с надписью "Установить" ;).

Шаг 2. Добавляем ресурсы. Для этого выбираем в главном меню Insert->Insert Resource (или Ctrl+R) и нажимаем кнопку Import, чтобы импортировать ресурс. В появившемся окне выбираем необходимые файлы, кроме того следует также отключить автоопределение ресурса путем переключения пункта "Open As" на значение "Custom". После выбора импортируемых файлов, средой будет выдан запрос на указание типа ресурсов. В появившемся окне Resource Type следует ввести вводим "EXE" (без кавычек). Будет создан новый ресурс типа "EXE", или множество ресурсов, если были выбраны несколько файлов. Редактирование свойств ресурса и изменение его идентификатора осуществляется путем щелчка правой кнопки мыши (я не левша...) и выбора меню Properties. В моем примере распаковывается три картинки JPEG. Скопируем их в подпапку res нашего проекта, а затем импортируем в проект вышеописанным способом. Должны появиться три новых ресурса типа " EXE" с идентификаторами IDR_EXE1, IDR_EXE2, IDR_EXE3. Для удобства можем изменить значения этих идентификаторов на более удобные: пусть это будут INDEX1_RES, INDEX2_RES и INDEX3_RES.

Шаг 3. Процедура извлечения ресурсов.
Напишем функцию извлечения ресурса, используя Api-функции. Указанная функция может будет иметь приблизительно такой вид.
Код:
bool ExtractResource(HINSTANCE hInstance,LPCTSTR lpName,LPCTSTR lpType,char* Destination)
{
  HRSRC Res;
  HGLOBAL hGlb;
  char * buff;
  HANDLE hFile;
  DWORD temp;

  if (!(Res=FindResource(hInstance,lpName,lpType)))return FALSE;
  if (!(hGlb=LoadResource(hInstance,Res)))return FALSE;
  buff=(char*)LockResource(hGlb);
  if (!(hFile=CreateFile(Destination,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL)))
  {
	  FreeResource(hGlb);
	  return FALSE;
  }
  WriteFile(hFile,buff,SizeofResource(hInstance,Res),&temp,NULL);
  CloseHandle(hFile);
  FreeResource(hGlb);
  return TRUE;
}
Функция ExtractResource имеет четыре формальных параметра: "сущность" (дескриптор экземпляра модуля) hInstance, в которой находится искомый ресурс, имя ресурса lpName, тип ресурса lpType, и имя файла, куда будет распакован ресурс – параметр Destination. Api-функция FindResource пытается найти ресурс с указанным именем и типом ресурса в модуле, определяемом переменной hInstance , и в случае удачного исхода, возвращает дескриптор ресурса – это переменная Res типа HRSRC. Дело в том, что ресурс можно в память загрузить только после того, как он будет найден. Собственно загрузка ресурса в память производится функцией LoadResource, которая возвращает дескриптор глобального блока памяти hGlb. Переменная hGlb – это переменная, скорее нужная самой ОС для идентификации блока памяти, в который загружен ресурс (в самой ОС есть таблицы блоков памяти, где ведется учет всех свободных и занятых блоков), чем для нас. Система может перемещать блоки или выгружать на диск в файл подкачки. И чтобы не случилось неприятной ситуации, когда некоторая программа работает с ресурсом в оперативной памяти, в то время как система пытается его выгружать (или уже выгружает), ресурс надо зафиксировать в памяти. Для этой цели имеется Api-функция LockResource, которая блокирует ресурс, загруженный в память, и возвращает указатель на адрес начала блока памяти, в котором хранится этот ресурс. Остается теперь только скопировать содержимое памяти в файл. Для работы с файлами средствами WinApi надо получить дескриптор файла, и для этого существует функция CreateFile. Кстати говоря, эта функция универсальна, и позволяет работать не только с файлами в обычном смысле этого слова, но также и из реальными и виртуальными устройствами, COM, LPT, USB - портами и т.д. Функция имеет довольно много параметров, все их описывать достаточно скучно, однако замечу, что флаг GENERIC_WRITE говорит, что мы намерены записывать информацию в файл, а CREATE_ALWAYS – файл будет всегда создаваться заново. Другие возможные значения флагов можно посмотреть в справочнике (Р. Саймон, Справочник по WinApi; или Д. Рихтер, Программирование на WinApi для профессионалов). После удачного получения дескриптора файла записываем информацию функцией WriteFile, и освобождаем полученные системные ресурсы. Как видим, все довольно просто. *flowers*
Функция ExtractResource выполняет "грязную" работу по извлечению ресурса (любого типа и из любого модуля). Мы напишем функцию, которая облегчает доступ к функции ExtractResource, автоматически заполняя некоторые параметры, передаваемые в ExtractResource. Назовем эту функцию ExtractFileExe.
Код:
void ExtractFileExe(char* Dir,char*Name,int Res)
{
     char buff[MAX_PATH];
	 wsprintf(buff,"%s\\%s",Dir,Name);
     if (!ExtractResource(GetModuleHandle(NULL),MAKEINTRESOURCE(Res),"EXE",buff))
	 {
		 char temp[MAX_PATH];
		 wsprintf(temp,"Ошибка извлечения файла %s",Name);
		 AfxMessageBox(temp,MB_OK|MB_ICONERROR);
	 }
}
Функция ExtractFileExe имеет три параметра: директорию, куда будет распакован файл, имя, которое будет дано файлу, и числовой идентификатор ресурса в программе. Тут заслуживает внимания макрос MAKEINTRESOURCE – он позволяет по числовому идентификатору ресурса в программе получить его символьное имя, которое затем должно быть передано функции поиска ресурса FindResource. Api-функция GetModuleHandle с параметром NULL позволяет получить дескриптор нашего главного (и единственного в программе) модуля.

Таким образом, оператор
Код:
ExtractFileExe(path,"index1.jpeg",INDEX1_RES)
извлечет ресурс с числовым идентификатором INDEX1_RES и сохранит его в файл index1.jpeg, путь к которому определяет строка path.

Полностью рабочий пример находится в архиве VCInstall.rar.
(C) siryksv, 2009-2010.
 

Вложения

  • Screen.JPG
    Screen.JPG
    21 KB · Просмотры: 457
  • VCInstall.rar
    231,1 KB · Просмотры: 303

MaGoth

★★★★★★★★★★★
Администратор
Регистрация
7 Янв 2003
Сообщения
19.371
Благодарности
7.802
Баллы
995
Хотелось бы узнать, нужно ли развивать эту тему? Я мог бы описать создание инсталлера, используя те же ресурсы, но на VC++ (только уже не используя классы Delphi, а на чистом WinApi 8)). Также можно бы рассмотреть вопросы работы с реестром в системе Delphi (а также и через WinApi), создания деинсталлирующей программы, создания ярлыков и т.д. Что скажете? ::)
Вопрос касаемый инсталлера, но немного из другой темы/плоскости. На сколько хорошо С++ знаешь, и работал ли с форматами файлов звука, их конвертации из одного в другой!? ::)
 

siryksv

Участник форума
Регистрация
5 Окт 2009
Сообщения
567
Благодарности
1
Баллы
225
MaGoth. Отписал в ЛС.*flowers*
 

sil69

Новичок
Регистрация
1 Апр 2010
Сообщения
2
Благодарности
0
Баллы
145
Скажите пожалуйста. Как сделать чтобы текст лицензионного соглашения выводился либо из файла либо многострочный? vc++
 

siryksv

Участник форума
Регистрация
5 Окт 2009
Сообщения
567
Благодарности
1
Баллы
225
sil69, ну вот пример решения. Текст лицензии должен находиться в файле License.txt, который лежит в одном каталоге из программой.
В функцию
Код:
BOOL CVCInstallDlg::OnInitDialog()
, там где я обьявлял переменную License:
, надо вставить подгрузку из файла. Например, так:
Код:
	HANDLE hFile; char *License;
	if ((hFile=CreateFile( "License.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ))!=INVALID_HANDLE_VALUE)
	{
		DWORD FileSize=GetFileSize(hFile,0), tempRead;
		License=(char *)malloc(FileSize);
		if (!ReadFile( hFile, License, FileSize, &tempRead, NULL )) License="Файл лицензии не прочитан.";
		CloseHandle(hFile);

	}else License="Файл лицензии не существует, или не удалось получить доступ";

	m_License.SetWindowText(License);
Во вложении VCInstallDlg.cpp.rar я даю исправленный вариант файла VCInstallDlg.cpp, куда внес указанное изменение.
 

Вложения

  • VCInstallDlg.cpp.rar
    2,2 KB · Просмотры: 227

sil69

Новичок
Регистрация
1 Апр 2010
Сообщения
2
Благодарности
0
Баллы
145
Я конечно жутко извиняюсь, но можно также рассмотреть вопрос создания ярлыков???
 

siryksv

Участник форума
Регистрация
5 Окт 2009
Сообщения
567
Благодарности
1
Баллы
225
Я конечно жутко извиняюсь, но можно также рассмотреть вопрос создания ярлыков???
Ок. Где-то у меня лежали свои готовые коды, правда они на Дельфи, а не на VC, так что, как только найду, подробно опишу создание ярлыка со всеми тонкостями. А пока я бы порекомендовал Вам воспользоваться Гуглом ;);). Посмотрите вот тут: http://www.cracklab.ru/pro/cpp.php?r=vspp&d=zgrt107 или http://forum.codenet.ru/showthread.php?t=22681 (верхние два поста). Правда, я сам не пробовал, так что не могу поручиться, работают ли описанные способы. Там исходники одинаковы, на обоих сайтах ;).
Только следует иметь в виду, что для работы с COM и Shell Objects надо вызывать
Код:
CoInitialize(NULL)
, иначе приведенный выше по ссылкам код работать не будет (по второй ссылке - там уже проинициализировано). Если Вы хотите создавать ярлыки в спец. каталогах (например, Рабочем столе пользователя), то надо предварительно узнать путь к этому каталогу, чтобы было что передавать в первом параметре приведенной выше функции создания ярлыка. Пути можно узнавать либо используя реестр: "HKEY_CURRENT_USER\Software\MicroSoft\Windows\CurrentVersion\Explorer\Shell Folders", либо же воспользовавшись функцией SHGetSpecialFolderPath. Например, так:
Код:
char Desk[MAX_PATH];
if (SHGetSpecialFolderPath(0,Desk,CSIDL_DESKTOPDIRECTORY,false))
    {
       //у нас теперь в Desk находится путь. Что-то с ним делаем...
    }
Да, тут константа CSIDL_DESKTOPDIRECTORY - для получения пути к текущему рабочему столу. Другие константы находим у Мелкософта:http://msdn.microsoft.com/en-us/library/aa931257.aspx, или где-нибудь еще...;)

Если что-то будет непонятно, спрашивайте.
 
Сверху Снизу