Створення бібліотеки за допомогою MASM ;бібліотечний файл mydll_asm.asm ; DLL містить функцію для перекодування числа в текст .386 .model flat ; Оголошуємо власну API функцію використовуючи узгодження stdcall. ; Всі API функції використовують узгодження stdcall public _num2txt@4 .data ; Змінна mystr містить буфер для конвертованого рядку тексту mystr db 2 dup(0) .code ; DLL файл має точку входу, яка приймає три 4 байтні параметри: ; 1) ідентифікатор; 2) причину виклику; 3) зарезервований параметр. ; Ми їх не використовуємо, тому просто повертаємо ненульове значення через регістр EAX ; і очищуємо стек. _start@12: mov eax,1 ret 12 ; наша процедура _num2txt@4 proc ; виймаємо з стеку і зберігаємо точку повернення в есх pop ecx ; виймаємо з стеку і зберігаємо єдиний параметр функції в edx pop edx ; повертаємо в стек точку повернення з есх push ecx
; здійснюємо приведення значення десяткового числа до його еквіваленту в форматі ASCII add edx,48 ; зберігаємо результат в змінній mystr попередньо занісши її адресу в eax mov eax, offset mystr mov [eax],dl ; оскільки параметр функції вже вийняли зі стеку, то очищати стек не потрібно ret _num2txt@4 endp
end _start@12 ---------------------------------------------------------------------------------------------------------- ; в файлі mydll.def треба написати всі назви функцій, що є в dll LIBRARY mydll_asm EXPORTS num2txt ----------------------------------------------------------------------------------------------------------- ; bat файл, що здійснюватиме створення dll: makeit_dll.bat @echo off if exist mydll_asm.obj del mydll_asm.obj if exist mydll_asm.dll del mydll_asm.dll \masm32\bin\ml /c /coff mydll_asm.asm \masm32\bin\Link /SUBSYSTEM:WINDOWS /DLL /DEF: mydll_asm.def mydll_asm.obj ;del mydll_asm.obj ;del mydll_asm.exp dir mydll_asm.* pause Програма, що викликає функцію з створеної бібліотеки (MASM) ; Програма для тестування calldll.asm. Використовується неявне зв’язування. .386 .model flat ; Додаємо необхідні бібліотеки, в тому числі і створену власноруч. ; В процесі компіляції з використанням неявного зв’язування ; наявність dll файла не є необхідною, ; але є необхідною при виконанні програми. ; Це стосується програм написаних на С, С++ і асемблері includelib mydll_asm.lib includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib ; Оголошуємо функції, які використовуємо ; в фотрматі: ; ключове слово extrn ; префікс __imp_ ; назва функції _num2txt ; кількість даних, які передаються в стек у байтах @4 (1 параметр в даному випадку) ; :dword extrn __imp__num2txt@4:dword ; Оголошуємо API функцію MessageBoxA, що приймає параметри в кодуванні ASCII extrn __imp__MessageBoxA@16:dword MessageBox equ __imp__MessageBoxA@16 ; Оголошуємо API функцію ExitProcess, що завершує виконання прграми extrn __imp__ExitProcess@4:dword ExitProcess equ __imp__ExitProcess@4 .data mess_title db 'Number to string',0 .code _start: ; Конвертуємо число 2 в строку викликавши функцію _num2txt з dll push 2 call __imp__num2txt@4
; Завершуємо виконання програми push 0 call ExitProcess end _start ------------------------------------------------------------------------------------------------------------ ; bat файл, що здійснюватиме створення тестової програми makeit_exe.bat @echo off if exist calldll.obj del calldll.obj if exist calldll.exe del calldll.exe \masm32\bin\ml /c /coff calldll.asm \masm32\bin\Link /SUBSYSTEM:WINDOWS calldll.obj del calldll.obj dir mydll.* pause --------------------------------------------------------------------
Рис.1. Результат виконання програми. Створення бібліотеки за допомогою MSVS 2005 // Для створення dll вибрати тип проекту “DLL” // Файл my_dll.h. Містить прототип функції бібліотеки char mystr[2] = {0};
// Необхідно експортувати С інтерфейс extern "C" __declspec(dllexport) char* num2txt(int n); -------------------------------------------------------------------------------------------------------- // Файл my_dll.cpp. Містить функцію бібліотеки #include "my_dll.h" extern "C" __declspec(dllexport) char* num2txt(int n) { mystr[0] = n + 48; return mystr; } /* використання __declspec(dllexport) дозволяє нам не створювати файл mydll.def, оскільки вмістиме цього файлу включається в бібліотеку. __declspec(dllexport) створює файли в форматі «С++». Якщо такий формат не є бажаним, то необхідно створити файл *.def самостійно, або використати extern "C" в оголошені функції. */ Програма, що викликає функцію з створеної бібліотеки (MSVS). Неявне зв’язування // Використовується тип проекту “Windows Application” // Файл my_dll.h. Містить прототип функції бібліотеки char mystr[2] = {0};
// Необхідно експортувати С інтерфейс extern "C" __declspec(dllexport) char* num2txt(int n); ----------------------------------------------------------------------------------------------------- // Файл neyavno.cpp. #include <Windows.h> #include "my_dll.h" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR lpCmdLine, int nCmdShow) { char mess_title[] = "Number to string"; char * pszStr; pszStr = num2txt(2); MessageBoxA(0, pszStr, mess_title, 0x40); ExitProcess(0); } -------------------------------------------------------------------- Програма, що викликає функцію з створеної бібліотеки (MSVS) Неявне зв’язування. Використання __declspec( dllimport ) #include <Windows.h> extern "C" __declspec(dllimport) char* num2txt(int n); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR lpCmdLine, int nCmdShow) { char mess_title[] = "Number to string"; char * pszStr; pszStr = num2txt(2); MessageBoxA(0, pszStr, mess_title, 0x40); ExitProcess(0); } -------------------------------------------------------------------- __declspec( dllimport ) __declspec( dllimport ) використовується лише разом з функціями і вказує на те, що дана функція оголошена в бібліотеці. Таке оголошення дозволяє не генерувати thunk "перехідник" (невелику секцію коду, що виконує перетворення (наприклад, типів) чи забезпечує виклик 32-розрядного коду з 16-розрядного і навпаки). При цьому відбувається пришвидшення виконання програми (див. MSDN). __declspec( dllimport ) заборонено використовувати при створенні функції в бібліотеці, використовується лише при її виклику. Без __declspec( dllimport ) компілятор генерує код: int main(void) { func1(); } наступним чином: call func1 і лінкер генерує такий код: call 0x4000000 ; Адреса 'func1'
Якщо лінкер не знає адреси функції, яка є в dll, тоді генерується помилка. В 32 розрядному середовищі лінкер згенерує наступний «перехідник» для виклику функції: 0x40000000: jmp DWORD PTR __imp_func1 З використанням __declspec( dllimport ) компілятор генерує код: __declspec(dllimport) void func1(void); int main(void) { func1(); } наступним чином: call DWORD PTR __imp_func1 В цьому коді відсутній «перехідник», що дозволяє пришвидшити виконання програми Програма, що викликає функцію з створеної бібліотеки (MSVS) Явне зв’язування. /* Програма здійснює виклик функції num2txt, що знаходиться в бібліотеках, що створені в MASM і MSVS2005. Код, що працює з написаним в MASM має суфікс _asm. */ #include <Windows.h> int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR lpCmdLine, int nCmdShow) { //оголошення вказівників на функції // функція написана в MSVS використовує узгодження __cdecl char* (__cdecl* num2txt)(int); // функція написана в MASM використовує узгодження __stdcall або WINAPI // WINAPI має таке оголошення в windef.h // #define WINAPI __stdcall char* (WINAPI* num2txt_asm)(int); char mess_title[] = "Number to string"; char * pszStr; //завантажити бібліотеку HINSTANCE hLib=LoadLibrary("my_dll.DLL"); HINSTANCE hLib_asm=LoadLibrary("mydll_asm.DLL"); //якщо бібліотека не завантажена, то вивести повідомлення і вийти if(!hLib || !hLib_asm) { MessageBoxA(0, "Cannot load dll", "Error", 0x40); ExitProcess(0); } // завантажити адресу функції з іменем "num2txt" num2txt = (char *(__cdecl*)(int))GetProcAddress((HMODULE)hLib, "num2txt"); num2txt_asm = (char *(__stdcall*)(int))GetProcAddress((HMODULE)hLib_asm, "num2txt"); // перевірити чи функції завантажено if (num2txt) { pszStr = num2txt(5); MessageBoxA(0, pszStr, mess_title, 0x40); } else MessageBoxA(0,"Error loading function", "Error", MB_OK); if (num2txt_asm) { pszStr = num2txt(3); MessageBoxA(0, pszStr, mess_title, 0x40); } else MessageBoxA(0,"Error loading function", "Error", MB_OK);
// вивантажити бібліотеки FreeLibrary((HMODULE)hLib); FreeLibrary((HMODULE)hLib_asm); //завершити виконання ExitProcess(0); }