Использование DllEntryPoint

В данной статье я хотел бы рассмотреть два вопроса — во первых, рассмотреть пример использования точки входа библиотеки (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. В принципе наименования файлов могут быть такими как нравится вам, в коде я буду придерживаться указанной схемы. В прикрепленном файле вы найдете исходные коды проекта.
Для получения версии файла используется функция, получающая в качестве параметра путь к файлу библиотеки и ссылку на строку, куда нужно записать результат. Код функции:
[code]
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;
}
}

[/code]
Обратите внимание на точку входа:
[code]
//Глобальная переменная для имени библиотеки
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;
}

[/code]

И собственно код из модуля loadforms
[code]
//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 = «???? ver.» + versioninfo;
fmMain->ShowModal();
delete fmMain;
return NULL;
}

#pragma package(smart_init)

//—————————————————————————

[/code]
На сегодня все.
При перепечатке ссылка на ресурс обязательна.
Обсуждение статьи на форуме