[Назад] [Далее]

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

Кроме обычных приложений в Windows появился специальный тип файла — динамические библиотеки (DLL). DLL — это файл, содержащий процедуры и данные, которые доступны программам, обращающимся к нему. Например, все системные функции Windows, которыми мы пользовались, на самом деле были процедурами, входящими в состав таких библиотек, — kernel32.dll, user32.dll, comdlg32.dll и т.д. Динамические библиотеки позволяют уменьшить использование памяти и размер исполнимых файлов для тех случаев, когда несколько программ (или даже несколько копий одной и той же программы) используют одну и ту же процедуру. Можно считать, что DLL — это аналог пассивной резидентной программы, с тем лишь отличием, что DLL не находится в памяти, если ни одна программа, его использующая, не загружена.

С точки зрения программирования на ассемблере DLL — это самый обычный исполнимый файл формата РЕ, отличающийся только тем, что при входе в него в стеке находятся три параметра (идентификатор DLL-модуля, причина вызова процедуры и зарезервированный параметр), которые надо удалить, например командой ret 12. Кроме этой процедуры в DLL входят и другие, часть которых можно вызывать из других программ. Список этих экспортируемых процедур должен быть задан во время компиляции DLL, и поэтому команды для компиляции нашего следующего примера будут отличаться от обычных.


Компиляция MASM:

ml /с /coff /Cp /D_MASM_ dllrus.asm
link dllrus.obj @dllrus.lnk

Содержимое файла dllrus.lnk:

/DLL
/entry:start
/subsystem:windows
/export:koi2win_asm
/export:koi2win
/export:koi2wins_asm
/export:koi2wins

Компиляция TASM:

tasm /m /x /ml /D_TASM_ dllrus.asm
tlink32 -Tpd -c dllrus.obj,,,,dllrus.def

Содержимое файла dllrus.def:

EXPORTS koi2win_asm koi2win koi2wins koi2wins_asm

Компиляция WASM:

wasm dllrus.asm
wlink @dllrus.dir

Содержимое dllrus.dir:

file    dllrus.obj
form    windows nt DLL
exp     koi2win_asm,koi2win,koi2wins_asm,koi2wins


; dllrus.asm
; DLL для Win32 - перекодировщик из koi8 в ср1251
        .386
        .model     flat
; функции, определяемые в этом DLL
ifdef _MASM_
        public _koi2win_asm@0   ; koi2win_asm - перекодирует символ в AL
        public _koi2win@4       ; CHAR WINAPI koi2win(CHAR symbol)
        public _koi2wins_asm@0  ; koi2wins_asm - перекодирует строку в [ЕАХ]
        public _koi2wins@4      ; VOID WINAPI koi2win(CHAR * string)
else
        public koi2win_asm      ; те же функции для TASM и WASM
        public koi2win
        public koi2wins_asm
        public koi2wins
endif

        .const
; таблица для перевода символа из кодировки KOI8-r (RFC1489)
; в кодировку Windows (cp1251),
; таблица только для символов 80h - FFh
; (то есть надо будет вычесть 80h из символа, преобразовать его командой xlat
; и снова добавить 80h)
k2w_tbl db         16 dup(0)       ; символы, не существующие в ср1251,
        db         16 dup(0)       ; перекодируются в 80h
        db         00h, 00h, 00h, 38h, 00h, 00h, 00h, 00h
        db         00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
        db         00h, 00h, 00h, 28h, 00h, 00h, 00h, 00h
        db         00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
        db         7Eh, 60h, 61h, 76h, 64h, 65h, 74h, 63h
        db         75h, 68h, 69h, 6Ah, 6Bh, 6Ch, 6Dh, 6Eh
        db         6Fh, 7Fh, 70h, 71h, 72h, 73h, 66h, 62h
        db         7Ch, 7Bh, 67h, 78h, 7Dh, 79h, 77h, 7Ah
        db         5Eh, 40h, 41h, 56h, 44h, 45h, 54h, 43h
        db         55h, 48h, 49h, 4Ah, 4Bh, 4Ch, 4Dh, 4Eh
        db         4Fh, 5Fh, 50h, 51h, 52h, 53h, 46h, 42h
        db         5Ch, 5Bh, 47h, 58h, 5Dh, 59h, 57h, 5Ah

        .code
; процедура DLLEntry. Получает три параметра - идентификатор, причину вызова
; и зарезервированный параметр. Нам не нужен ни один из них
_start@12:
        mov        al,1                ; надо вернуть ненулевое число в ЕАХ
        ret        12

; процедура BYTE WINAPI koi2win (BYTE symbol) -
; точка входа для вызова из С
ifdef _MASM_
    _koi2win@4     proc
else
    koi2win        proc
endif
        pop        ecx                 ; обратный адрес в ЕСХ
        pop        eax                 ; параметр в ЕСХ (теперь стек очищен
                                       ; от параметров!)
        push       ecx                 ; обратный адрес вернуть в стек для RET
; здесь нет команды RET - управление передается следующей процедуре
ifdef _MASM_
    _koi2win@4     endp
else
    koi2win        endp
endif

; процедура koi2win_asm
; точка входа для вызова из ассемблерных программ:
; ввод: AL - код символа в KOI
; вывод: AL - код этого же символа в WIN
ifdef _MASM_
    _koi2win_asm@0 proc
else
    koi2win_asm    proc
endif
        test       al,80h              ; если символ меньше 80h (старший бит 0),
        jz         dont_decode         ; не перекодировать,
        push       ebx                 ; иначе -
        mov        ebx,offset k2w_tbl
        sub        al,80h              ; вычесть 80h,
        xlat                           ; перекодировать
        add        al,80h              ; и прибавить 80h
        pop        ebx
dont_decode:
        ret                            ; выйти
ifdef _MASM_
    _koi2win_asm@0 endp
else
    koi2win_asm    endp
endif

; VOID koi2wins(BYTE * koistring) -
; точка входа для вызова из С
ifdef _MASM_
    _koi2wins@4    proc
else
    koi2wins       proc
endif
        pop        ecx                 ; адрес возврата из стека
        pop        eax                 ; параметр в ЕАХ
        push       ecx                 ; адрес возврата в стек
ifdef _MASM_
    _koi2wins@4    endp
else
    koi2wins       endp
endif
; точка входа для вызова из ассемблера:
; ввод: ЕАХ - адрес строки, которую надо преобразовать из KOI в WIN
ifdef _MASM_
    _koi2wins_asm@0  proc
else
    koi2wins_asm     proc
endif
        push       esi                 ; сохранить регистры, которые
                                       ; нельзя изменять
        push       edi
        push       ebx
        mov        esi,eax             ; приемник строк
        mov        edi,eax             ; и источник совпадают
        mov        ebx,offset k2w_tbl
decode_string:
        lodsb                          ; прочитать байт,
        test       al,80h              ; если старший бит 0,
        jz         dont_decode2        ; не перекодировать,
        sub        al,80h              ; иначе - вычесть 80h,
        xlat                           ; перекодировать
        add        al,80h              ; и добавить 80h
dont_decode2:
        stosb                          ; вернуть байт на место,
        test       al,al               ; если байт - не ноль,
        jnz        decode_string       ; продолжить
        pop        ebx
        pop        edi
        pop        esi
        ret
ifdef _MASM_
    _koi2wins_asm@0   endp
else
    koi2wins_asm      endp
endif
        end        _start@l2

Как видно из примера, нам пришлось назвать все процедуры по-разному для различных ассемблеров. В случае MASM понятно, что все функции должны иметь имена типа _start@12, а иначе программам, использующим их, придется обращаться к функциям с именами типа _Jmp_start, то есть такой DLL нельзя будет использовать из программы, написанной на Microsoft С. В случае TASM и WASM процедуры могут иметь неискаженные имена (и более того, wlink.exe не позволяет экспортировать имя переменной, содержащее символ @), так как их компиляторы берут имена процедур не из библиотечного файла, а прямо из DLL при помощи соответствующей программы — implib или wlib.

Итак, чтобы воспользоваться полученным DLL, напишем простую программу, которая перекодирует одну строку из КОI-8r в Windows ср1251.

; dlldemo.asm
; Графическое приложение для Win32, демонстрирующее работу с dllrus.dll,
; выводит строку в KOI8 и затем в ср1251, перекодированную функцией koi2wins
include def32.inc
include user32.inc
include kernel32.inc
includelib dllrus.lib
ifndef _MASM_
        extrn      koi2win__asm:near   ; определения для функций из DLL для
        extrn      koi2win:near        ; TASM и WASM
        extrn      koi2wins_asm:near   ; (хотя для WASM было бы эффективнее
        extrn      koi2wins:near       ; использовать __imp__koi2win, выделив
else                                   ; его в отдельный условный блок),
        extrn      __imp__koi2win_asm@0:dword    ; а это для MASM
        extrn      __imp__koi2win@4:dword
        extrn      __imp__koi2wins_asm@0:dword
        extrn      __imp__koi2wins@4: dword
koi2win_asm        equ                 __imp__koi2win_asm@0
koi2win            equ                 __imp__koi2win@4
koi2wins_asm       equ                 __imp__koi2wins_asm@0
koi2wins           equ                 __imp__koi2wins@4
endif

        .386
        .model     flat
        .const
title_string1      db    "koi2win demo: string in KOI8",0
title_string2      db    "koi2win demo: string in cp1251",0

        .data
koi_string         db    0F3h,0D4h,0D2h,0CFh,0CBh,0C1h,20h,0CEh,0C1h
                   db    20h,0EBh,0EFh,0E9h,2Dh,28h,0
        .code
_start:
        push       MB_OK
        push       offset title_string1   ; заголовок окна MessageBox
        push       offset koi_string      ; строка на KOI
        push       0
        call       MessageBox
        mov        eax,offset koi_string
        push       eax
        call       koi2wins
        push       MB_OK
        push       offset title_string2
        push       offset koi_string
        push       0
        call       MessageBox
        push       0                      ; код выхода
        call       ExitProcess            ; конец программы

        end        _start

Этот небольшой DLL может оказаться очень полезным для расшифровки текстов, приходящих из сети Internet или других систем, в которых используется кодировка KOI8. Воспользовавшись таблицами из приложения 1, вы можете расширить набор функций dllrus.dll, вплоть до перекодировки из любого варианта в какой угодно.


п»ї
"target=_blank><\/a>") //-->