Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Как дописать в ехе файл данные


Автор: SAVe 8.11.2007, 10:46
Здравствуйте.

Есть такая проблема:

Есть готовый ехе файл. Для его работы нужны справочники которые генерируются из БД в виде XML файлов. Нужно каким то образом засунуть их в сам ехе файл. Ну и естественно он должен при запуске прочитать эти данные. Хотелось бы записать это в ресурсы .  Подскажите пожалуйста как это сделать? Где можно найти  информацию  по фомату ехе файла (где код , где ресурсы , где данные)?

Автор: chaos 8.11.2007, 12:04
даже если у Вас получится засунуть данные в ехе файл(в чом сомневаюсь). Вам придется еще править код! те программа должна знать откуда брать Ваши данные.

ИМХО "не ищите легких путей"

Автор: Peter 8.11.2007, 12:24
Цитата(chaos @  8.11.2007,  12:04 Найти цитируемый пост)
даже если у Вас получится засунуть данные в ехе файл(в чом сомневаюсь). Вам придется еще править код! те программа должна знать откуда брать Ваши данные

На самом деле этот вариант не такой уж и страшный. Я им пользовался.
В программе заводим переменную целого типа, в которую вносим размер exe-шника в байтах. После компиляции сверяем правильность её значения; если оно не такое, то исправляем и перекомпилируем - от этого размер программы не меняется. Внутри программы открываем сам exe-шник через fopen или что-нибудь подобное (GetModuleFileName поможет нам получить путь к нему) и читаем данные, начиная с указанной позиции. Осталось не забыть приклеить данные сзади к exe-шнику.

Автор: chaos 8.11.2007, 12:30
Цитата(Peter @ 8.11.2007,  14:24)
В программе заводим переменную целого типа

вопрос на засыпку: что делать если исходников у вас нет?

Автор: SAVe 8.11.2007, 12:37
Спасибо Peter . Я то же склонялся к именно такой реализации. Смущает только вот что - не загрузятся ли данные которые я приклею в сегмент кода или данных ?



Автор: chaos 8.11.2007, 12:41
SAVe, не совсем понятно что вы хотите:
сначала вы говорите что Есть готовый ехе файл потом вы говорите что метод подходит и последнее: если генерируются данные из БД - как часто вы будете извращатся таким способом - засовывая данные в ехе ?

Автор: Peter 8.11.2007, 12:41
Цитата(chaos @  8.11.2007,  12:30 Найти цитируемый пост)
вопрос на засыпку: что делать если исходников у вас нет?

Об этом речи не было.

Цитата(SAVe @  8.11.2007,  12:37 Найти цитируемый пост)
не загрузятся ли данные которые я приклею в сегмент кода или данных ?

С какой стати? EXE-шник сам по себе останется нетронутым. Если сзади что-то приклеить, в работе программы ничего не собьётся.

Автор: chaos 8.11.2007, 12:47
Цитата(Peter @ 8.11.2007,  14:41)
Об этом речи не было.


Цитата(SAVe @ 8.11.2007,  12:46)

Есть готовый ехе файл. 

Автор: SAVe 8.11.2007, 13:19
Извините если немного некорректно обЪяснил...
Внесу некоторую ясность.

Цитата

Есть готовый ехе файл. 


 
Имеется ввиду что я сам его собираюсь сделать (т.е. исходники будут).  Но данные с которыми он работает (XML справочники) могут меняться. Клиенту же (их много) нужно выдать только ехе , а не ехе и набор  XML файлов. Поэтому делать это (брать данные из базы и дописывать в ехе) прийдётся часто. 

Если же дописанные данные на работе ехе файла никак не отразятся - то это вполне подходящий вариант.

Если кто знает как всё это добро (XML справочники)  засунуть в ресурсы ехе файла или изменить сегмент данных программы - расскажите как . И если кто в курсе где посмотреть формат ехе файла ,то же киньте ссылочку пожалуйста.

Автор: bsa 8.11.2007, 14:22
SAVe, в комплекте с твоей средой разработки должен поставляться компилятор ресурсов. Найди формат RC файлов (исходник ресурсного). Там все достаточно просто. тебе нужно будет из XML делать RC, затем RC скомпилировать в RES, и RES прилинковать к программе во время сборки.

Имхо, проще воспользоваться WinRAR. Создаешь SFX архив и пишешь простейший скрипт на языке этого WinRAR SFX, который после распаковки запустит твою прогу, а после завершения удалит все за собой.

Автор: xvr 8.11.2007, 16:19
Цитата(SAVe @ 8.11.2007,  10:46)
Здравствуйте.

Есть такая проблема:

Есть готовый ехе файл. Для его работы нужны справочники которые генерируются из БД в виде XML файлов. Нужно каким то образом засунуть их в сам ехе файл. Ну и естественно он должен при запуске прочитать эти данные. Хотелось бы записать это в ресурсы .  Подскажите пожалуйста как это сделать? Где можно найти  информацию  по фомату ехе файла (где код , где ресурсы , где данные)?

См. функции BeginUpdateResource, UpdateResource  и EndUpdateResource
Для доступа - как обычно LoadResource

Автор: SAVe 9.11.2007, 18:10
Пошел по по пути использовнаия BeginUpdateResource, UpdateResource  и EndUpdateResource.
Сделал 2 ехе файла ехе1 и ехе2. Один (ехе1) содержит ресурс который нужно обновить. Второй файл  (ехе2) пытается при старте обновить этот ресурс в файле ехе1.
Всё  отрабатывае. Все проверки показывают что обновление прошло удачно. Но ресурсы файла ехе1 почему то не обновляются. Почему ? Что не так делаю ?

Попутно ещё вопрос - как достать пустой ресурс  ? Т.е. если в нём пусто он не отображается на вкладке resources. Хотя имеет ID (только чека выставлена unused).  Функция FindResource его не находит. Как его сделать используемым ?

Автор: xvr 9.11.2007, 22:39
Цитата(SAVe @ 9.11.2007,  18:10)
Пошел по по пути использовнаия BeginUpdateResource, UpdateResource  и EndUpdateResource.
Сделал 2 ехе файла ехе1 и ехе2. Один (ехе1) содержит ресурс который нужно обновить. Второй файл  (ехе2) пытается при старте обновить этот ресурс в файле ехе1.
Всё  отрабатывае. Все проверки показывают что обновление прошло удачно. Но ресурсы файла ехе1 почему то не обновляются. Почему ? Что не так делаю ?

Телепатов нет - код на бочку, будем изучать smile

Цитата

Попутно ещё вопрос - как достать пустой ресурс  ? Т.е. если в нём пусто он не отображается на вкладке resources. Хотя имеет ID (только чека выставлена unused).  Функция FindResource его не находит.
Судя по всему в выходном exe его нет совсем. То что студия зарезервировала ID для него, еще не значит, что линкер поместил то, чего нет в выходной файл.
Цитата

Как его сделать используемым ?
Добавить какое-нибудь содержимое.

Автор: SAVe 12.11.2007, 16:51
Привожу код:

В ехе1 просто читаю ресурс (предварительно добавил его конечно т.е. он не пустой):

 
Код

::OnInitDIalog()
{

.....

HINSTANCE hInst = ::GetModuleHandle(NULL);
    HRSRC hRes = ::FindResource(hInst,MAKEINTRESOURCE(130),"XML");
    LPVOID  pRes = (LPVOID)::LoadResource(hInst,hRes);

    char* pData = (char*)pRes;

// Текст выводится
    MessageBox(pData);
.....
}


Текст выводится.

В ехе2 делаю так (в ехе2 содержится свой ресурс IDR_XML1 который я пытаюсь записать в ехе1):

Код


// Читаю новый текст ресурса который нужно записать в ехе1
HMODULE hInst = ::AfxGetInstanceHandle();
    HRSRC hRes = ::FindResource(hInst,MAKEINTRESOURCE(IDR_XML1),"XML");
    LPVOID  pRes = (LPVOID)::LoadResource(hInst,hRes);
// Размер новых данных
    DWORD resSize = ::SizeofResource(hInst,hRes);
    char * pData = (char*)pRes ;
    
// Новый текст ресурса выводится
    MessageBox(pData);

// Получаю  HANDLE файла в котором нужно обновить ресурсы
    HANDLE resHandle = ::BeginUpdateResource("d:\\Test\\Debug\\Test.exe",FALSE);

//resHandle не NULL

        BOOL UpdRes = ::UpdateResource(resHandle,MAKEINTRESOURCE
(130),"XML",MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL),pData,resSize);

// UpdRes после выполнения = 1


        BOOL EndRes = ::EndUpdateResource(resHandle,FALSE);
// EndRes после выполнения = 1



Вроде бы всё благополучно проходит.

Реально же при старте ехе1 выводится испорченный текст ресурса (дописана к исходному тексту какая то ерунда). При установке в функции 
BeginUpdateResource (..., TRUE) файл ехе1 просто портится и не стартует. 

Проверяю сам код файла exe1 (test.exe). Там в конец файла дописан правильный текст. Но за ним куча какого то левого текста PADDINGX

Автор: xvr 12.11.2007, 17:52
Цитата(SAVe @ 12.11.2007,  16:51)
В ехе2 делаю так (в ехе2 содержится свой ресурс IDR_XML1 который я пытаюсь записать в ехе1):

Код


// Читаю новый текст ресурса который нужно записать в ехе1
HMODULE hInst = ::AfxGetInstanceHandle();
    HRSRC hRes = ::FindResource(hInst,MAKEINTRESOURCE(IDR_XML1),"XML");


Согласно Талмуду (MSDN) чтение ресурса должно проводится так:

Код

HGLOBAL hg = (LPVOID)::LoadResource(hInst,hRes);
LPVOID pRes=LockResource(hg);

Кроме того, параметр lpData функции UpdateResource:
Цитата

Pointer to the resource data to be inserted into the executable file. If the resource is one of the predefined types, the data must be valid and properly aligned. Note that this is the raw binary data stored in the executable file, not the data provided by LoadIcon, LoadString, or other resource-specific load functions. All data containing strings or text must be in Unicode format; lpData must not point to ANSI data.
, не знаю, считает ли Windows ресурс с типом 130 - string or text. Можно попробовать тип ресурса RT_RCDATA (над ним точно издеваться не будут)
Цитата

Вроде бы всё благополучно проходит.
Реально же при старте ехе1 выводится испорченный текст ресурса (дописана к исходному тексту какая то ерунда). 
Похоже ресурс дописывается в хвост к предыдущему содержимому - попробуй сначала удалить предыдущее содержимое (вызвав UpdateResource с NULL в качестве lpData)
Цитата

При установке в функции 
BeginUpdateResource (..., TRUE) файл ехе1 просто портится и не стартует. 
Еще бы - ты ему ВСЕ ресурсы убил.
Цитата

Проверяю сам код файла exe1 (test.exe). Там в конец файла дописан правильный текст. Но за ним куча какого то левого текста PADDINGX
Это нормально

Автор: SAVe 13.11.2007, 19:06
Вобщем о том как в итоге проблема была "зарыта в могилу" (может кому пригодится).

Итак с BeginUpdateResource, UpdateResource  и EndUpdateResource всё таки ничего не получилось. Пришлось  разбираться с форматом Portable Executable ( поиск по строке  PE в MSDN выдаёт нужные статьи).
Идея вот в чём :
Сами данные записываем просто в конец файла (В любом виде. Я просто строки писал).
Но возникает проблема - с какого адреса ( с какого смещения в файле ) нужно читать свои данные ? Для определения этого смещения пришлось проанализировать  заголовок файла и определить где заканчивается последний блок данных которые были записаны при компиляции.

Вот код ехе2 который модифицируе файл ехе1 дописывая данные в его конец:

Код


BOOL CResUpdDlg::OnInitDialog()
{
    .................

// Я предварительно добавил в ресурсы ехе2 файл с данными которые хочу добавить в ехе1
// Получаю эти данные
    HMODULE hInst = ::AfxGetInstanceHandle();
    HRSRC hRes = ::FindResource(hInst,MAKEINTRESOURCE(IDR_XML1),"XML");
    HGLOBAL  hGlob = (LPVOID)::LoadResource(hInst,hRes);
    LPVOID pRes = ::LockResource(hGlob);
// Размер новых данных
    DWORD resSize = ::SizeofResource(hInst,hRes);
// Получил сами данные
    char * pData = (char*)pRes ;
// Открываю на запись файл в который буду записывать новые данные    
    CFile file("d:\\My\\MFC\\Test\\Test\\Debug\\Test.exe",CFile::modeWrite);
// Переходим в конец файла    
    file.SeekToEnd();
// Записываем
    file.Write((LPCTSTR)pData,resSize);

    ::UnlockResource(hGlob);
        
    return TRUE;  // ¶З¦^ TRUEЎA°Ј«D±z№п±±Ёо¶µі]©wµJВI
}



 Вот код который находит "оригинальный " конец файла ехе1 (при старте самого ехе1 )и читает новые данные:

Код


BOOL CTestDlg::OnInitDialog()
{

...........

    // Находим "оригинальный " конец  ехе из которого запустились
    DWORD OriginalEndOfFile = GetOriginalEndOfFile();

    CFile file("d:\\My\\MFC\\Test\\Test\\Debug\\Test.exe",CFile::modeRead);
//Реальная длина файла
    DWORD RealEndOfFile = file.GetLength();
// Размер дописанных данных
    DWORD BuffSize = RealEndOfFile - OriginalEndOfFile;
    char * str = new char[BuffSize + 1];
// Переходим на свои данные (которые сами записали)
    file.Seek(OriginalEndOfFile,CFile::begin);
// Читаем их
    file.Read(str,BuffSize);
    str[BuffSize] = 0;
// Отображаем
    MessageBox(str);
    delete[]  str;
// Выход
    ::PostQuitMessage(0);
    
    return TRUE;  
}

DWORD CTestDlg::GetOriginalEndOfFile()
{
    HANDLE hFile;
    HANDLE hFileMapping;
    LPVOID lpFileBase;
    PIMAGE_DOS_HEADER dosHeader;
    PIMAGE_NT_HEADERS pNTHeader;
    PIMAGE_SECTION_HEADER pSectionHeader;
    PIMAGE_SECTION_HEADER pLastSectionHeader;
    DWORD FileAddress;
    CString filename;
    
    FileAddress = 0;
// Файл из которого будем читать свои данные (он же в данный момент и запущен)
    filename = "d:\\My\\MFC\\Test\\Test\\Debug\\Test.exe";

    // Создаём дескриптор файла
    hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
                    
    if ( hFile == INVALID_HANDLE_VALUE )
    {
        //printf("Couldn't open file with CreateFile()\n");
        return 0;
    }

    // Мапим файл в память и получаем дескриптор. 
    // На диске данные расположены с теми же смещениями что и в памяти.
    hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
    if ( hFileMapping == 0 )
    {
        CloseHandle(hFile);
        //printf("Couldn't open file mapping with CreateFileMapping()\n");
        return 0;
    }

    // Получаем указатель на базовый адресс по которому расположен файл
    lpFileBase = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
    if ( lpFileBase == 0 )
    {
        CloseHandle(hFileMapping);
        CloseHandle(hFile);
        //printf("Couldn't map view of file with MapViewOfFile()\n");
        return 0;
    }

    //printf("Dump of file %s\n\n", filename);
    
    // Получаем указатель на DOS заголовок 
    dosHeader = (PIMAGE_DOS_HEADER)lpFileBase;
    // Получаем указатель на NT заголовок
    pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)dosHeader + dosHeader->e_lfanew);
    // Получаем указатель на SectionTable. 
    // Идёт сразу за IMAGE_NT_HEADER. Поэтому увеличиваем  указатель pNTHeader (имеет тип PIMAGE_NT_HEADERS) 
    // на 1. Тем самым перепрыгиваем NT заголовок и попадаем на SectionTable.
    pSectionHeader = (PIMAGE_SECTION_HEADER)(pNTHeader + 1);
    // Количество секций
    WORD nSections = pNTHeader->FileHeader.NumberOfSections;
    // Получаем указатель на последнюю секцию т.к. они расположены в порядке увеличения RVA 
    // (увеличиваем указатель на "количество секций" - 1)
    pLastSectionHeader = (PIMAGE_SECTION_HEADER)(pSectionHeader + (nSections - 1 ));
    // Прибавляем к смещению начала данных данных последней секции относительно начала файла (PointerToRawData) 
    // размер данных последней секции (SizeOfRawData) и таким образом получаем 
    // смещение от начала файла (адресс) первого байта тех данных которые мы записали в конец файла.
    FileAddress = pLastSectionHeader->PointerToRawData + pLastSectionHeader ->SizeOfRawData;

    UnmapViewOfFile(lpFileBase);
    CloseHandle(hFileMapping);
    CloseHandle(hFile);

    return FileAddress;


}



У меня получилось так. Желаю удачи всем тем кто захочет это использовать.
Спасибо всем кто поучаствовал в обсуждении темы.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)