Использование DllEntryPoint
4 апреля 2009 года
В данной статье я хотел бы рассмотреть два вопроса - во первых, рассмотреть пример использования точки входа библиотеки (DllEntryPoint), во вторых - продемонстрировать один из способов, как определить версию файла библиотеки из ее самой. Собственно определение версии - это задача достаточно частная - на самом деле используя подобную технологию можно например маппить в память образ длл, модифицировать и... ну впрочем ограничимся получением версии.
И так начнем с точки входа (Dll Entry Point). Хочу обратить внимание, что я рассматриваю сейчас только случай когда загрузка библиотеки происходит динамически, если вам нужна статическия линковка - поэкспериментируйте самостоятельно.
Точка входа (DllEntryPoint) очень удобный иструмент для настройки загрузки и выгрузки вашей библиотеки. Функция точки входа вызывается при каждой загрузке и выгрузке библиотеки и получает три параметра - дескриптор библиотеки (HINSTANCE), флаг причины вызова(DWORD fwdreason) и детализация вызова (LPVOID lpvReserved). Хочу отметить, что если вы не используете VC-стиль для dll, то наименование и типы могут немного отличаться.
Как это использовать? Дескриптор библиотеки может быть использован при вызове различных функций - например той же функции получения версии файла.
Флаг загрузки может принимать следующие значения:
- DLL_PROCESS_ATTACH - При присоединении к адресному пространству текущего процесса, в результате его запуска или вызова функции LoadLibrary
- DLL_THREAD_ATTACH - Данный процесс создает новый поток
- DLL_PROCESS_DETACH - Завершение процесса либо вызов FreeLibrary
- DLL_THREAD_DETACH - Завершение потока
Параметр lpvReserved позволяет определить каким образом загружена библиотека - статически или динамически. Если параметр имеет значение отличное от NULL - библиотека загружена статически, если же NULL - то динамически (с использованием LoadLibrary/FreeLibrary).
Как это использовать? В качестве примера я приведу код, который позволяет получить номер версии библиотеки и вывести его в заголовке модального окна. Для этого вы должны создать новый проект типа DllWizard, Source Type - C++, Use VCL, VC++Style Dll. В проект добавьте юнит (File/New/Unit), сохраните его под названием loadforms.cpp. Так же добавьте форму, дайте ей название fmMain, файл сохраните как main.cpp. Из файла с DllEntryPoin можете прочесть и удалить обширный комментарий и сохранить файл как maindll.cpp, а сам проект - так как нравится вам - например versiontest. В принципе наименования файлов могут быть такими как нравится вам, в коде я буду придерживаться указанной схемы. В прикрепленном файле вы найдете исходные коды проекта.
Для получения версии файла используется функция, получающая в качестве параметра путь к файлу библиотеки и ссылку на строку, куда нужно записать результат. Код функции:
void FileVersion(char* file,String &version){ VS_FIXEDFILEINFO pvsf; DWORD dwHandle; DWORD cchver; BOOL bret; cchver = GetFileVersionInfoSize( file, &dwHandle ); if( cchver != 0 ) { char* pver = new char[ cchver ]; bret = GetFileVersionInfo( file, dwHandle, cchver, pver ); if( bret ) { UINT uLen; void *pbuf; bret = VerQueryValue( pver, "\\", &pbuf, &uLen ); if( bret ) { memcpy( &pvsf, pbuf, sizeof( VS_FIXEDFILEINFO ) ); char* fileVersion = new char[ cchver ]; sprintf( fileVersion, "%01.1d.%01.1d.%01.1d.%01.1d", HIWORD( pvsf.dwFileVersionMS ), LOWORD( pvsf.dwFileVersionMS ), HIWORD( pvsf.dwFileVersionLS ), LOWORD( pvsf.dwFileVersionLS ) ); version = (String)fileVersion; delete [] fileVersion; } } delete[] pver; } }
Обратите внимание на точку входа:
//Глобальная переменная для имени библиотеки char *VersionInfo = new char[512]; BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved) { switch(fwdreason){ case DLL_PROCESS_ATTACH: { //Библиотека загружена через LoadLibrary? if(lpvReserved == NULL){ //Получаем имя модуля GetModuleFileName(hinstDLL,VersionInfo,512); } break; } case DLL_PROCESS_DETACH: { if(lpvReserved == NULL) //Если вызывана FreeLibrary - очищаем память и выходим delete[] VersionInfo; } } return 1; }
И собственно код из модуля loadforms
//loadforms.h #ifndef loadformsH #define loadformsH //--------------------------------------------------------------------------- #include //--------------------------------------------------------------------------- extern "C" __declspec(dllexport) HWND ShowDllForm(); #endif //loadforms.cpp #pragma hdrstop #include "loadforms.h" #include "main.h" #include extern char *VersionInfo; void FileVersion(char* file,String &version); typedef DWORD(__import *pGetFileVersionInfoSize)(LPCTSTR lptstrFilename,LPDWORD lpdwHandle); typedef DWORD(__import *pGetFileVersionInfo)(LPCTSTR lptstrFilename,DWORD dwHandle,DWORD dwLen,LPVOID lpData); typedef DWORD(__import *pVerQueryValue)(LPCVOID pBlock,LPCTSTR lpSubBlock,LPVOID *lplpBuffer,PUINT puLen); pGetFileVersionInfoSize fGetFileVersionInfoSize; pGetFileVersionInfo fGetFileVersionInfo; pVerQueryValue fVerQueryValue; void FileVersion(char* file,String &version){ VS_FIXEDFILEINFO pvsf; DWORD dwHandle; DWORD cchver; BOOL bret; cchver = GetFileVersionInfoSize( file, &dwHandle ); if( cchver != 0 ) { char* pver = new char[ cchver ]; bret = GetFileVersionInfo( file, dwHandle, cchver, pver ); if( bret ) { UINT uLen; void *pbuf; bret = VerQueryValue( pver, "\\", &pbuf, &uLen ); if( bret ) { memcpy( &pvsf, pbuf, sizeof( VS_FIXEDFILEINFO ) ); char* fileVersion = new char[ cchver ]; sprintf( fileVersion, "%01.1d.%01.1d.%01.1d.%01.1d", HIWORD( pvsf.dwFileVersionMS ), LOWORD( pvsf.dwFileVersionMS ), HIWORD( pvsf.dwFileVersionLS ), LOWORD( pvsf.dwFileVersionLS ) ); version = (String)fileVersion; delete [] fileVersion; } } delete[] pver; } } //--------------------------------------------------------------------------- HWND ShowDllForm() { fmMain = new TfmMain(Application); String versioninfo; FileVersion(VersionInfo,versioninfo); fmMain->Caption = "Aaii ver." + versioninfo; fmMain->ShowModal(); delete fmMain; return NULL; } #pragma package(smart_init) //---------------------------------------------------------------------------