Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скляров И. Изучаем Assembler за 7 дней (2010).pdf
Скачиваний:
1335
Добавлен:
23.02.2015
Размер:
2.11 Mб
Скачать

http://www.sklyaroff.ru

179

invoke MessageBox,NULL,ADDR About_string,OFFSET

AppName,MB_OK

.ELSEIF ax==IDM_HELLO

invoke MessageBox,NULL,ADDR Hello_string,OFFSET

AppName,MB_OK

.ELSEIF ax==IDM_GOODBYE

invoke MessageBox,NULL,ADDR Goodbye_string,OFFSET

AppName,MB_OK

.ELSE

invoke DestroyWindow,hWnd

.ENDIF

.ELSE

invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret

.ENDIF

 

xor

eax,eax

WndProc endp

ret

 

 

 

end

start

 

Листинг 7.7. Файл с определением ресурса меню (menu.rc)

#define IDM_ABOUT

1

#define IDM_HELLO

2

#define IDM_GOODBYE

3

#define IDM_EXIT

4

FirstMenu MENU

{

POPUP "&File"

{

MENUITEM "&Приветствие!",IDM_HELLO MENUITEM "&Прощание!",IDM_GOODBYE MENUITEM SEPARATOR

MENUITEM "В&ыход",IDM_EXIT

}

MENUITEM "&About",IDM_ABOUT

}

7.7. Динамические библиотеки

Динамические библиотеки (DLL — dynamic link libraries) играют большую роль в Windows, можно даже сказать, что на DLL построена сама Windows.

Мы уже пользовались системными библиотеками kernel32.dll, user32.dll, gdi32.dll для вызова системных API функций, однако программист может создавать собственные динамические библиотеки.

DLL — это файлы, которые в отличие от обычных исполняемых файлов, содержат в

себе лишь только набор автономных функций для вызова из других приложений или

DLL6.

6 Динамическая библиотека может содержать также ресурсы.

http://www.sklyaroff.ru

180

Зачем нужны динамические библиотеки?

DLL позволяют уменьшить использование памяти и размер исполняемых файлов. Без DLL коды всех используемых программой функций пришлось бы заносить прямо в исполняемый файл. Причем если несколько программ используют одни и те же функции, одинаковые коды функций пришлось бы заносить в каждую программу. DLL позволяет эффективно решить проблему, т. к. DLL загружается в память в единственном экземпляре, а программы, которым нужны функции, подключаются к DLL во время выполнения. При этом в самих программах содержатся только вызовы необходимых функций. Если программа одна использует DLL, то сразу после завершения программы DLL также выгружается из памяти. Но если DLL используют несколько программ, то она останется в памяти, пока ее не выгрузит последняя из использующих ее программ.

Чтобы программа смогла выполнить функцию, находящуюся в библиотеке, необходимо код этой функции сначала загрузить в память вызывающего процесса и передать адрес, по которому находится этот код.

Это можно осуществить двумя способами:

неявное линкование DLL;

явная загрузка DLL.

Ниже каждый способ будет рассмотрен подробно, но сначала узнаем, как создать саму динамическую библиотеку.

7.7.1. Простейшая динамическая библиотека

Влистинге приведен исходный код простейшей динамической библиотеки. Данная DLL просто выводит сообщение при загрузке библиотеки в память, при ее выгрузке из памяти, а также при вызове функции TestFunction.

Влюбой динамической библиотеке должна быть определена так называемая "точка входа", т. е. процедура (функция), которая автоматически будет вызываться при загрузке и выгрузке динамической библиотеки. В листинге 7.8 точкой входа является функция DllEntry. Эта функция принимает 3 параметра:

hInstance — идентификатор DLL, присваиваемый системой;

reason — причина вызова (значения, которые может принимать этот параметр см. ниже);

reserved1 — зарезервирован и поэтому равен NULL.

Четыре возможные значения, которые может принимать второй параметр

(определены в windows.inc):

DLL_РROCESS_DETACH

equ 0

DLL_РROCESS_ATTACH

equ 1

DLL_THREAD_ATTACH

equ

2

DLL_THREAD_DETACH

equ

3

DLL_РROCESS_ATTACH — сообщает, что DLL загружается в адресное пространство процесса.

DLL_РROCESS_DETACH — сообщает, что DLL выгружается из адресного пространства процесса.

DLL_THREAD_ATTACH — сообщает, что текущий процесс создает новый поток. Такое сообщение посылается всем динамическим библиотекам, загруженным к этому времени процессом.

DLL_THREAD_DETACH — сообщает, что поток в адресном пространстве процесса уничтожается.

Обратите также внимание, что процедура входа должна возвращать ненулевое значение:

mov eax,TRUE

Значение TRUE в файле windows.inc объявлено как

TRUE equ 1

Для компиляции DLL понадобится создать def-файл (листинг 7.9), в котором необходимо просто указать имя библиотеки (после ключевого слова LIBRARY) и

http://www.sklyaroff.ru

181

экспортируемые функции (после ключевого слова EXPORTS). Если быть точным DEF-файл необходим для создания статической lib-библиотеки (DLLSkeleton.lib), которая нужна только при неявной загрузке DLL. Для явной загрузки DLL, статическая lib-библиотека не нужна, соответственно в этом случае компилировать DLL можно без DEF-файла.

Следующие две командные строки выполняют компиляцию DLL: ml /c /coff DLLSkeleton.asm

link /SUBSYSTEM:WINDOWS /DLL /DEF:DLLSkeleton.def DLLSkeleton.obj

Листинг 7.8. Простейшая DLL-библиотека (DLLSkeleton.asm)

.386

.model flat,stdcall option casemap:none

include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib

.data

 

HelloTitle

db "Пример вызова функции из DLL",0

HelloMsg

db "Это сообщение из DLL",0

LoadMsg

db "DLL загружена",0

UnloadMsg

db "DLL выгружена",0

ThreadCreated

db "Поток создается в этом процессе",0

ThreadDestroyed db "Поток уничтожается в этом процессе",0

.code

DllEntry PROC hInstance:HINSTANCE,reason:DWORD,reserved1:DWORD

.if reason==DLL_PROCESS_ATTACH

 

invoke MessageBox,NULL,addr LoadMsg,addr HelloTitle,MB_OK

.elseif reason==DLL_PROCESS_DETACH

 

invoke MessageBox,NULL,addr UnloadMsg,addr HelloTitle,MB_OK

.elseif reason==DLL_THREAD_ATTACH

invoke

MessageBox,NULL,addr ThreadCreated,addr HelloTitle,MB_OK

.else

; DLL_THREAD_DETACH

invoke

MessageBox,NULL,addr ThreadDestroyed,addr HelloTitle,MB_OK

.endif

 

mov

eax,TRUE

ret

 

DllEntry ENDP

TestFunction PROC

invoke MessageBox,NULL,addr HelloMsg,addr HelloTitle,MB_OK

ret

TestFunction ENDP

END DllEntry

http://www.sklyaroff.ru

182

Листинг 7.9. def-файл (DLLSkeleton.def)

LIBRARY DLLSkeleton

EXPORTS TestFunction

7.7.2. Неявная загрузка DLL

В листинге 7.10 представлена программа, которая осуществляет неявную загрузку динамической библиотеки, исходный код которой показан в листинге 7.8. Для неявного связывания необходимо с помощью директивы INCLUDELIB подключить статическую библиотеку DLLSkeleton.lib. Эту библиотеку транслятор MASM создает автоматически при компиляции с def-файлом.

Следующие две командные строки выполняют компиляцию программы, демонстрирующую неявную загрузку DLL:

ml /c /coff usedll1.asm

link /SUBSYSTEM:WINDOWS usedll1.obj

Листинг 7.10. Программа, демонстрирующая неявное линкование DLL (usedll1.asm)

.386

.model flat,stdcall option casemap:none

include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib DLLSkeleton.lib includelib \masm32\lib\kernel32.lib

TestFunction PROTO

 

.code

 

start:

 

invoke

TestFunction

invoke

ExitProcess,NULL

end start

7.7.3. Явная загрузка DLL

Влистинге 7.11 представлена программа, которая осуществляет явную загрузку динамической библиотеки, исходный код которой показан в листинге 7.8.

Вслучае явной загрузки DLL вызывающая программа сама выполняет все манипуляции с DLL. Для того чтобы загрузить библиотеку в память, программа должна вызвать функцию LoadLibrary или LoadLibraryEx.

Функция LoadLibrary принимает единственный аргумент – имя динамической библиотеки, которую требуется загрузить. Описание из MSDN:

HMODULE WINAPI LoadLibrary(LPCTSTR lpFileName);

Функция LoadLibraryEx является расширенной версией LoadLibrary и принимает три аргумента, ее описание вы можете увидеть в MSDN, а мы обойдемся LoadLibrary.

После загрузки DLL программа должна определить адрес нужной функции в DLL — это делается с помощью функции GetProcAddress. Описание функции:

FARPROC WINAPI GetProcAddress( HMODULE hModule,

LPCSTR lpProcName

);

Как видно функция GetProcAddress принимает два параметра:

http://www.sklyaroff.ru

183

hModule — дескриптор DLL-библиотеки (полученный с помощью LoadLibrary);

lpProcName — имя процедуры, которую требуется вызвать из DLL.

В случае успешного выполнения GetProcAddress возвращает адрес процедуры в регистр EAX.

Когда DLL больше не нужна, программа может ее выгрузить с помощью функции

FreeLibrary или FreeLibraryAndExitThread. Однако при выходе из программы динамическая библиотека выгружается автоматически системой.

Что касается самой DLL (листинг 7.8), то, разумеется, она никаким изменениям не подвергается.

Следующие две командные строки выполняют компиляцию программы, демонстрирующую явную загрузку DLL:

ml /c /coff usedll2.asm

link /SUBSYSTEM:WINDOWS usedll2.obj

Листинг 7.11. Программа, демонстрирующая явное линкование DLL (usedll2.asm)

.386

.model flat,stdcall option casemap:none

include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\user32.lib

.data

 

LibName

db "DLLSkeleton.dll",0

FunctionName

db "TestFunction",0

DllNotFound

db "Не получается загрузить DLL",0

AppName

db "Загружается DLL",0

FunctionNotFound db "Функция TestFunction не найдена",0

 

.data?

 

hLib

dd

?

TestFunc dd

?

.code

start:

invoke LoadLibrary,addr LibName

.if eax==NULL

invoke MessageBox,NULL,addr DllNotFound,addr

AppName,MB_OK

.else

mov hLib,eax

invoke GetProcAddress,hLib,addr FunctionName

.if eax==NULL

invoke MessageBox,NULL,addr FunctionNotFound,addr

AppName,MB_OK

.else

call eax

.endif

invoke FreeLibrary,hLib

.endif

http://www.sklyaroff.ru

184

invoke ExitProcess,NULL

end start