Средства разработки приложений

         

Краткие комментарии к динамической библиотеке


Процедура libEntry является точкой входа в динамическую библиотеку, её не надо объявлять как экспортируемую, загрузчик сам определяет её местонахождение. LibEntry может вызываться в четырёх случаях:

  • при проецировании библиотеки в адресное пространство процесса (DLL_PROCESS_ATTACH);
  • при первом вызове библиотеки из потока (DLL_THREAD_ATTACH), например, с помощью функции LoadLibrary;
  • при выгрузке библиотеки потоком (DLL_THREAD_DETACH);
  • при выгрузке библиотеки из адресного пространства процесса (DLL_PROCESS_DETACH).

В нашем примере обрабатывается только первое из событий DLL_PROCESS_ATTACH. При обработке данного события библиотека запрашивает версию OS сохраняет её, а также свой handle of instance.

Библиотека содержит только одну экспортируемую функцию, которая собственно не требует пояснений. Вы, пожалуй, можете обратить внимание на то, как производится запись преобразованных значений. Интересна система адресации посредством двух регистров общего назначения: ebx + ecx, она позволяет нам использовать регистр ecx одновременно и как счётчик и как составную часть адреса.

Пример 3. Оконное приложение Файл dmenu.asm Ideal P586 Radix 16 Model flat struc WndClassEx cbSize dd 0 style dd 0 lpfnWndProc dd 0 cbClsExtra dd 0 cbWndExtra dd 0 hInstance dd 0 hIcon dd 0 hCursor dd 0 hbrBackground dd 0 lpszMenuName dd 0 lpszClassName dd 0 hIconSm dd 0 ends WndClassEx struc Point left dd 0 top dd 0 right dd 0 bottom dd 0 ends Point struc msgStruc hwnd dd 0 message dd 0 wParam dd 0 lParam dd 0 time dd 0 pt Point <> ends msgStruc MyMenu = 0065 ID_OPEN = 9C41 ID_SAVE = 9C42 ID_EXIT = 9C43 CS_VREDRAW = 0001 CS_HREDRAW = 0002 IDI_APPLICATION = 7F00 IDC_ARROW = 7F00 COLOR_WINDOW = 5 WS_EX_WINDOWEDGE = 00000100 WS_EX_CLIENTEDGE = 00000200 WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE OR WS_EX_CLIENTEDGE WS_OVERLAPPED = 00000000 WS_CAPTION = 00C00000 WS_SYSMENU = 00080000 WS_THICKFRAME = 00040000 WS_MINIMIZEBOX = 00020000 WS_MAXIMIZEBOX = 00010000 WS_OVERLAPPEDWINDOW = WS_OVERLAPPED OR \ WS_CAPTION OR \ WS_SYSMENU OR \ WS_THICKFRAME OR \ WS_MINIMIZEBOX OR \ WS_MAXIMIZEBOX CW_USEDEFAULT = 80000000 SW_SHOW = 5 WM_COMMAND = 0111 WM_DESTROY = 0002 WM_CLOSE = 0010 MB_OK = 0 PROCTYPE ptGetModuleHandle stdcall \ lpModuleName :dword PROCTYPE ptLoadIcon stdcall \ hInstance :dword, \ lpIconName :dword


PROCTYPE ptLoadCursor stdcall \ hInstance :dword, \ lpCursorName :dword PROCTYPE ptLoadMenu stdcall \ hInstance :dword, \ lpMenuName :dword PROCTYPE ptRegisterClassEx stdcall \ lpwcx :dword PROCTYPE ptCreateWindowEx stdcall \ dwExStyle :dword, \ lpClassName :dword, \ lpWindowName :dword, \ dwStyle :dword, \ x :dword, \ y :dword, \ nWidth :dword, \ nHeight :dword, \ hWndParent :dword, \ hMenu :dword, \ hInstance :dword, \ lpParam :dword PROCTYPE ptShowWindow stdcall \ hWnd :dword, \ nCmdShow :dword PROCTYPE ptUpdateWindow stdcall \ hWnd :dword PROCTYPE ptGetMessage stdcall \ pMsg :dword, \ hWnd :dword, \ wMsgFilterMin :dword, \ wMsgFilterMax :dword PROCTYPE ptTranslateMessage stdcall \ lpMsg :dword PROCTYPE ptDispatchMessage stdcall \ pmsg :dword PROCTYPE ptSetMenu stdcall \ hWnd :dword, \ hMenu :dword PROCTYPE ptPostQuitMessage stdcall \ nExitCode :dword PROCTYPE ptDefWindowProc stdcall \ hWnd :dword, \ Msg :dword, \ wParam :dword, \ lParam :dword PROCTYPE ptSendMessage stdcall \ hWnd :dword, \ Msg :dword, \ wParam :dword, \ lParam :dword PROCTYPE ptMessageBox stdcall \ hWnd :dword, \ lpText :dword, \ lpCaption :dword, \ uType :dword PROCTYPE ptExitProcess stdcall \ exitCode :dword extrn GetModuleHandleA :ptGetModuleHandle extrn LoadIconA :ptLoadIcon extrn LoadCursorA :ptLoadCursor extrn RegisterClassExA :ptRegisterClassEx extrn LoadMenuA :ptLoadMenu extrn CreateWindowExA :ptCreateWindowEx extrn ShowWindow :ptShowWindow extrn UpdateWindow :ptUpdateWindow extrn GetMessageA :ptGetMessage extrn TranslateMessage :ptTranslateMessage extrn DispatchMessageA :ptDispatchMessage extrn SetMenu :ptSetMenu extrn PostQuitMessage :ptPostQuitMessage extrn DefWindowProcA :ptDefWindowProc extrn SendMessageA :ptSendMessage extrn MessageBoxA :ptMessageBox extrn ExitProcess :ptExitProcess UDataSeg hInst dd ? hWnd dd ? IFNDEF VER1 hMenu dd ? ENDIF DataSeg msg msgStruc <> classTitle db 'Menu demo', 0 wndTitle db 'Demo program', 0 msg_open_txt db 'You selected open', 0 msg_open_tlt db 'Open box', 0 msg_save_txt db 'You selected save', 0 msg_save_tlt db 'Save box', 0 CodeSeg Start: call GetModuleHandleA, 0 ; не обязательно, но желательно mov [hInst],eax sub esp,SIZE WndClassEx ; отведём место в стеке под структуру mov [(WndClassEx esp).cbSize],SIZE WndClassEx mov [(WndClassEx esp).style],CS_HREDRAW or CS_VREDRAW mov [(WndClassEx esp).lpfnWndProc],offset WndProc mov [(WndClassEx esp).cbWndExtra],0 mov [(WndClassEx esp).cbClsExtra],0 mov [(WndClassEx esp).hInstance],eax call LoadIconA, 0, IDI_APPLICATION mov [(WndClassEx esp).hIcon],eax call LoadCursorA, 0, IDC_ARROW mov [(WndClassEx esp).hCursor],eax mov [(WndClassEx esp).hbrBackground],COLOR_WINDOW IFDEF VER1 mov [(WndClassEx esp).lpszMenuName],MyMenu ELSE mov [(WndClassEx esp).lpszMenuName],0 ENDIF mov [(WndClassEx esp).lpszClassName],offset classTitle mov [(WndClassEx esp).hIconSm],0 call RegisterClassExA, esp ; зарегистрируем класс окна add esp,SIZE WndClassEx ; восстановим стек ; и создадим окно IFNDEF VER2 call CreateWindowExA, WS_EX_OVERLAPPEDWINDOW, \ extended window style offset classTitle, \ pointer to registered class name offset wndTitle,\ pointer to window name WS_OVERLAPPEDWINDOW, \ window style CW_USEDEFAULT, \ horizontal position of window CW_USEDEFAULT, \ vertical position of window CW_USEDEFAULT, \ window width CW_USEDEFAULT, \ window height 0, \ handle to parent or owner window 0, \ handle to menu, or child-window identifier [hInst], \ handle to application instance 0 ; pointer to window-creation data ELSE call LoadMenu, hInst, MyMenu mov [hMenu],eax call CreateWindowExA, WS_EX_OVERLAPPEDWINDOW, \ extended window style offset classTitle, \ pointer to registered class name offset wndTitle, \ pointer to window name WS_OVERLAPPEDWINDOW, \ window style CW_USEDEFAULT, \ horizontal position of window CW_USEDEFAULT, \ vertical position of window CW_USEDEFAULT, \ window width CW_USEDEFAULT, \ window height 0, \ handle to parent or owner window eax, \ handle to menu, or child-window identifier [hInst], \ handle to application instance 0 ; pointer to window-creation data ENDIF mov [hWnd],eax call ShowWindow, eax, SW_SHOW ; show window call UpdateWindow, [hWnd] ; redraw window IFDEF VER3 call LoadMenuA, [hInst], MyMenu mov [hMenu],eax call SetMenu, [hWnd], eax ENDIF msg_loop: call GetMessageA, offset msg, 0, 0, 0 or ax,ax jz exit call TranslateMessage, offset msg call DispatchMessageA, offset msg jmp msg_loop exit: call ExitProcess, 0 public stdcall WndProc proc WndProc stdcall arg @@hwnd: dword, @@msg: dword, @@wPar: dword, @@lPar: dword mov eax,[@@msg] cmp eax,WM_COMMAND je @@command cmp eax,WM_DESTROY jne @@default call PostQuitMessage, 0 xor eax,eax jmp @@ret @@default: call DefWindowProcA, [@@hwnd], [@@msg], [@@wPar], [@@lPar] @@ret: ret @@command: mov eax,[@@wPar] cmp eax,ID_OPEN je @@open cmp eax,ID_SAVE je @@save call SendMessageA, [@@hwnd], WM_CLOSE, 0, 0 xor eax,eax jmp @@ret @@open: mov eax, offset msg_open_txt mov edx, offset msg_open_tlt jmp @@mess @@save: mov eax, offset msg_save_txt mov edx, offset msg_save_tlt @@mess: call MessageBoxA, 0, eax, edx, MB_OK xor eax,eax jmp @@ret endp WndProc end Start

Комментарии к программе

Здесь мне хотелось в первую очередь продемонстрировать использование прототипов функций API Win32.
Конечно их (а также описание констант и структур из API Win32) следует вынести в отдельные подключаемые файлы, поскольку, скорее всего Вы будете использовать их и в других программах. Описание прототипов функций обеспечивает строгий контроль со стороны компилятора за количеством и типом параметров, передаваемых в функции. Это существенно облегчает жизнь программисту, позволяя избежать ошибок времени исполнения, тем более, что число параметров в некоторых функциях API Win32 весьма значительно. Существо данной программы заключается в демонстрации вариантов работы с оконным меню. Программу можно откомпилировать в трёх вариантах (версиях), указывая компилятору ключи VER2 или VER3 (по умолчанию используется ключ VER1). В первом варианте программы меню определяется на уровне класса окна и все окна данного класса будут иметь аналогичное меню. Во втором варианте, меню определяется при создании окна, как параметр функции CreateWindowEx. Класс окна не имеет меню и в данном случае, каждое окно этого класса может иметь своё собственное меню. Наконец, в третьем варианте, меню загружается после создания окна. Данный вариант показывает, как можно связать меню с уже созданным окном. Директивы условной компиляции позволяют включить все варианты в текст одной и той же программы. Подобная техника удобна не только для демонстрации, но и для отладки. Например, когда Вам требуется включить в программу новый фрагмент кода, то Вы можете применить данную технику, дабы не потерять функционирующий модуль. Ну, и конечно, применение директив условной компиляции - наиболее удобное средство тестирования различных решений (алгоритмов) на одном модуле. Представляет определённый интерес использование стековых фреймов и заполнение структур в стеке посредством регистра указателя стека (esp). Именно это продемонстрировано при заполнении структуры WndClassEx. Выделение места в стеке (фрейма) делается простым перемещением esp: sub esp,SIZE WndClassEx Теперь мы можем обращаться к выделенной памяти используя всё тот же регистр указатель стека.При создании 16-битных приложений такой возможностью мы не обладали. Данный приём можно использовать внутри любой процедуры или даже произвольном месте программы. Накладные расходы на подобное выделение памяти минимальны, однако, следует учитывать, что размер стека ограничен и размещать большие объёмы данных в стеке вряд ли целесообразно. Для этих целей лучше использовать "кучи" (heap) или виртуальную память (virtual memory). Остальная часть программы достаточно тривиальна и не требует каких-либо пояснений. Возможно более интересным покажется тема использования макроопределений.

Содержание раздела