Использование 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)
//---------------------------------------------------------------------------
