На этом шаге мы приведем пример использования динамически подключаемой библиотеки различными процессами.
Рассмотрим теперь вопрос о том, как используют динамическую библиотеку различные экземпляры приложения или разные процессы. Если вы немного знакомы с принципом функционирования операционной системы Windows, то, возможно, такая постановка вопроса у вас вызовет недоумение. "У каждого приложения свое адресное пространство, куда загружается DLL", - скажете вы. Конечно, это не совсем рационально, но зато безопасно. О памяти мы еще подробно будем говорить, здесь же заметим, что, вообще говоря, приложение может инициализировать так называемую разделяемую память. Мы вернемся к этому вопросу еще неоднократно, сейчас же рассмотрим этот вопрос чисто технически, применительно к DLL. Рассмотрим конкретную ситуацию. Запускаемое приложение загружает динамически подключаемую библиотеку и вызывает процедуру из DLL, которая меняет данные, расположенные опять же в динамически подключаемой библиотеке. Запустим теперь второй экземпляр приложения. Оно загружает еще один экземпляр DLL. Могут быть ситуации, когда желательно, чтобы второе запущенное приложение "знало", что по команде первого приложения данные уже изменились. Ясно, что в этом случае данные, которыми оперирует DLL, должны быть общими. Технически это делается очень просто. У редактора свзей LINK eсть опция /section: имя, атрибуты, которая позволяет объявить явно свойства данной секции. Мы будем говорить о секциях далее, здесь же достаточно сказать, секция - это просто сегмент в старом понимании. В редакторе связей TLINK32 то же действие можно осуществить с помощью DEF-файла.
Представленные в примере программы весьма просты. По сути, они лишь иллюстрируют возможности использования разделяемой памяти. Перед выходом из процедуры DLL изменяется строка, хранящаяся в разделяемой секции памяти. При этом приложение не заканчивает своей работы. При запуске второго экземпляра приложения на экран выводится уже измененное первым приложением значение строки.
Файл pr75_1.asm - динамически подключаемая библиотека.
.386P ;Плоская модель. IFDEF MASM .MODEL FLAT, STDCALL ELSE .MODEL FLAT ENDIF PUBLIC DLLP1 IFDEF MASM ;MASM ;Прототипы внешних процедур. EXTERN MessageBoxA@16: NEAR ;Директивы компоновщику для подключения библиотек. ;Для компоновщика LINK.EXE. includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib ELSE ;TASM EXTERN MessageBoxA@: NEAR MessageBoxA@16 = MessageBoxA ;Для компоновщика TLINK32.EXE. includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' TEXT DB 'В динамической библиотеке',0 MS DB 'Сообщение',0 _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' ; [EBP+10H] - Резервный параметр. ; [ЕВР+0СН] - Причина вызова. ; [ЕВР+8] - Идентификатор DLL-модуля. DLLENTRY: MOV EAX,1 RET 12 ;------------------------------------- ;Адреса параметров ; [EBP+8] ; [ЕВР+0СН] DLLP1 PROC EXPORT PUSH EBP MOV EBP,ESP PUSH 0 PUSH OFFSET MS PUSH OFFSET TEXT PUSH 0 CALL MessageBoxA@16 ;Изменим строку, расположенную в разделяемой памяти. MOV TEXT,'И' MOV TEXT+1,'з' MOV TEXT+25,'и' POP EBP RET DLLP1 ENDP _TEXT ENDS END DLLENTRY
Файл pr75_2.asm - основной модуль, вызывающий процедуру из DLL.
.386P ;Плоская модель. .MODEL FLAT, STDCALL ;Константы. ;Прототипы внешних процедур. IFDEF MASM ;MASM EXTERN GetProcAddress@8:NEAR EXTERN LoadLibraryA@4:NEAR EXTERN FreeLibrary@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN MessageBoxA@16:NEAR ;Директивы компоновщику для подключения библиотек. ;Для компоновщика LINK.EXE. includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib ELSE ;TASM EXTERN GetProcAddress:NEAR EXTERN LoadLibraryA:NEAR EXTERN FreeLibrary:NEAR EXTERN ExitProcess:NEAR EXTERN MessageBoxA:NEAR GetProcAddress@8 = GetProcAddress LoadLibraryA@4 = LoadLibraryA FreeLibrary@4 = FreeLibrary ExitProcess@4 = ExitProcess MessageBoxA@16 = MessageBoxA ;Для компоновщика TLINK32.EXE. includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' TXT DB 'Ошибка динамической библиотеки',0 MS DB 'Сообщение',0 LIBR DB 'pr75_1.DLL',0 HLIB DD ? IFDEF MASM NAMEPROC DB '_DLLP1@0',0 ELSE NAMEPROC DB 'DLLP1',0 ENDIF _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' ; [EBP+10H] - Резервный параметр. ; [ЕВР+0СН] - Причина вызова. ; [ЕВР+8] - Идентификатор DLL-модуля. START: ;Загрузить библиотеку. PUSH OFFSET LIBR CALL LoadLibraryA@4 CMP EAX,0 JE _ERR MOV HLIB,EAX ;Получить адрес процедуры. PUSH OFFSET NAMEPROC PUSH HLIB CALL GetProcAddress@8 CMP EAX,0 JNE YES_NAME ;Сообщение об ошибке. _ERR: PUSH 0 PUSH OFFSET MS PUSH OFFSET TXT PUSH 0 CALL MessageBoxA@16 JMP _EXIT YES_NAME: CALL EAX PUSH 0 PUSH OFFSET MS PUSH OFFSET MS PUSH 0 CALL MessageBoxA@16 ;Закрыть библиотеку. ;Библиотека автоматически закрывается также ;при выходе из программы. PUSH OFFSET NAMEPROC PUSH HLIB CALL FreeLibrary@4 ;Выход. _EXIT: PUSH 0 CALL ExitProcess@4 _TEXT ENDS END START
Результат работы приложения изображен на рисунке 1:
Рис.1. Результат работы приложения
Трансляция динамической библиотеки:
ML /с /coff /DMASM pr75_1.asm LINK /SUBSYSTEM:WINDOWS /DLL /section:.data,SRW pr75_1.obj
TASM32 /ml pr75_1.asm TLINK32 -aa -Tpd pr75_1.obj,,,,pr75_1.def Содержимое файла pr75_1.def: SECTIONS.DATA SHARED EXPORTS DLLP1
Трансляция программы:
ML /с /coff /DMASM pr75_2.asm LINK /SUBSYSTEM:WINDOWS pr75_2.obj
TASM32 /ml pr75_2.asm TLINK32 -aa pr75_2.obj
Со следующего шага мы начнем рассматривать взаимодействие с ресурсами локальной сети.