Статьи по Assembler

       

Main.asm для mycall (ассемблер)


Это основной файл приложения MyCall на ассемблере. Этот файл в текстовом формате вместе со всеми остальными файлами, необходимыми для компиляции приложения MyCall, содержится в zip-файле mycallab.zip (15913 байт). Имеется также Инструкция программиста.

Для получения комментариев щелкaйте по тексту или пользуйтесь групповым управлением:

if(dhtml){document.write("Все комментарии: [+][-]    Открывать: [несколько]");}

;Включаемые файлы:

;@struct.inc - файл структурных макросов

;windows.inc - файл заголовков win32

;main.inc - файл заголовков приложения MyCall

include @struct.inc

include windows.inc

include main.inc

;ГЛАВНАЯ ФУНКЦИЯ ПРИЛОЖЕНИЯ

;///////////////////////////////////////////////////////////// WinMain

.data?



mw_class WNDCLASSEX{}

loop_message MSG{}

win_dim RECT{}

.const

mw_class_name db "MainWindowClass",0

my_message_name db "MyCallMessage",0

.code

WinMain PROC PUBLIC hinst,prev_hinst,command_line,cmd_show

mov dat_buffer,0

mov online,FALSE

;Получение дескриптора экземпляра приложения

;Необходимое действие, так как предполагается компиляция приложения без

;подключения runtime-библиотеки. Подробнее...

invoke GetModuleHandleA,NULL

mov hinst,eax

;Создание главного окна

;Обычное действие, с которого начинаются большинство приложений.

;Единственное отличие в том, что в качестве главного окна в MyCall

;используется окно диалога, описанного в файле ресурсов

;Регистрируется класс главного окна:

mov mw_class.cbSize,sizeof(WNDCLASSEX)

mov mw_class.style,NULL

mov mw_class.lpfnWndProc,offset superprocedure

mov mw_class.cbClsExtra,0

mov mw_class.cbWndExtra,DLGWINDOWEXTRA

mov eax,hinst

mov mw_class.hInstance,eax

invoke LoadIconA,eax,103

mov mw_class.hIcon,eax

mov mw_class.hIconSm,NULL

invoke LoadCursorA,NULL,IDC_ARROW

mov mw_class.hCursor,eax

mov mw_class.hbrBackground,COLOR_WINDOW

mov mw_class.lpszMenuName,NULL

mov mw_class.lpszClassName,offset mw_class_name


@if(ecx==3)

mov ecx,0

@endif

@endif

@if(ecx==0)

@if(ebx==0)

@if(byte ptr[esi]!=0)

@if(byte ptr[esi]!=1)

@push ecx,edx,ebx,esi

invoke SendMessageA,conn_window,CB_ADDSTRING,0,esi

@pop ecx,edx,ebx,esi

inc edx

@if(edx>=MAX_CON)

@break

@endif

@endif

@endif

@endif

@endif

inc ebx

inc esi

@endw

;По данным файла mycall. ini устанавливается прошлая позиция списка conn_window

@if(current_con>=edx)

mov current_con,0

@endif

invoke SendMessageA,conn_window,CB_SETCURSEL,current_con,0

;По состоянию списка conn_window формируются остальные списки

call change_con

;ЦИКЛ ОЖИДАНИЯ СООБЩЕНИЙ И ЗАВЕРШЕНИЕ ПРИЛОЖЕНИЯ

;Неотъемлемый элемент приложений для Windows. В MyCall никаких особенностей не имеет

;Поскольку MyCall собирается без runtime-библиотеки, для завершения работы

;обязательно использовать функцию ExitProcess. Подробнее...

msg_loop:

invoke GetMessageA,offset loop_message,NULL,0,0

@if(!eax)

invoke ExitProcess,loop_message.wParam

@endif

invoke TranslateMessage,offset loop_message

invoke DispatchMessageA,offset loop_message

jmp msg_loop

bad:

invoke fatal,eax

invoke ExitProcess,EXIT_COMMON_ERROR

WinMain ENDP

;ОКОННАЯ ПРОЦЕДУРА

;Стандартный элемент приложений для Windows. В MyCall особенностей не имеет.

;///////////////////////////////////////////////////////////// Оконная процедура

.data?

win_pos RECT{}

.code

superprocedure PROC window_from,message,w_param,l_param

mov eax,message

;Обработка сообщения WM_COMMAND, передаваемого элементами управления диалога

@if(eax==WM_COMMAND)

mov eax,w_param

mov cl,16

shr eax,cl

;Обработка уведомления CBN_SELCHANGE, передаваемого списками при изменении позиции:

;1000: список соединений conn_window. Изменяет содержание phon_window и user_window

;1001: список телефонов phon_window. Устанавливает новый телефон для текущего соединения con_phone[current_con]

;1002: список логинов user_window. Устанавливает новый логин для текущего соединения con_user[current_con]

@if(eax==CBN_SELCHANGE)



mov eax,w_param

and eax,0ffffh

@if(ax==1000)

mov eax,conn_window

and current_con,0ffh

mov esi,offset current_con

@elseif(ax==1001)

mov eax,phon_window

mov esi,offset con_phone

add esi,current_con

@elseif(ax==1002)

mov eax,user_window

mov esi,offset con_user

add esi,current_con

@else

jmp sp_nok

@endif

push esi

invoke SendMessageA,eax,CB_GETCURSEL,0,0

pop esi

@if(eax==CB_ERR)

xor eax,eax

@endif

mov [esi],al

call change_con

jmp sp_ok

@endif

; Обработка уведомления BN_CLICKED, передаваемого кнопкой при клике:

;в состоянии online=TRUE прекращает дозвон (разрывает соединение) и включает списки

;в состоянии online=FALSE отключает списки и начинает дозвон

@if(eax==BN_CLICKED)

mov eax,w_param

and eax,0ffffh

@if(eax==1003)

@if(online)

invoke EnableWindow,butt_window,FALSE

call ras_hangup

invoke EnableWindow,butt_window,TRUE

push FALSE

call disable_controls

@else

push TRUE

call disable_controls

call ras_dial

@endif

jmp sp_ok

@endif

@endif

jmp sp_nok

;Обработка сообщения WM_USER, используемого в MyCall нитью монитора разрыва.

;Обнаружив факт разрыва соединения, нить монитора передает WM_USER. Если был коннект,

;то включаются списки, в противном случает выполняется повторный дозвон

@elseif(eax==WM_USER)

call ras_hangup

@if(l_param)

push FALSE

call disable_controls

@else

call ras_dial

@endif

jmp sp_ok

;Обработка сообщения WM_MOVE: запоминание новой позиции окна

;для последующей записи ее в файл mycall.ini

@elseif(eax==WM_MOVE)

invoke GetWindowRect,main_window,offset win_pos

@if(eax)

push win_pos.left

pop main_win_left

push win_pos.top

pop main_win_top

@else

mov eax,l_param

and eax,0ffffh

mov main_win_left,eax

mov eax,l_param

mov cl,16

rcr eax,cl

and eax,0ffffh

mov main_win_top,eax

@endif

jmp sp_ok

;При завершении работы приложения записать файл mycall.ini,

;и освободить память

@elseif(eax==WM_DESTROY)

call save_ini

@if(dat_buffer)

invoke GlobalFree,dat_buffer

@endif

invoke PostQuitMessage,EXIT_NORMAL



jmp sp_ok

; При закрытии главного окна заблокировать кнопку

;и разорвать соединение

@elseif(eax==WM_CLOSE)

invoke EnableWindow,butt_window,FALSE

@if(online)

call ras_hangup

@endif

jmp sp_nok

;Обработка глобального оконного сообщения, используемого для

;взаимодействия экземпляров приложения. Получив это сообщение,

;экземпляр, запущенный первым, сообщает дескриптор своего главного окна.

;Сообщение, полученное вторым, принимает от первого дескриптор его главного окна,

;выводит его на первый план и завершается. Подробнее...

@if(eax==my_message)

mov ebx,w_param

@if(ebx!=main_window)

@if(!l_param)

invoke SendMessageA,HWND_BROADCAST,eax,main_window,1

@else

invoke SetForegroundWindow,w_param

invoke ExitProcess,EXIT_OVERLOADED

@endif

@endif

jmp sp_ok

@endif

;Возврат необработанных сообщений системе

sp_nok:

invoke DefWindowProcA,window_from,message,w_param,l_param

ret

;Завершение оконной процедуры для обработанных сообщений

sp_ok:

xor eax,eax

ret

superprocedure ENDP

;ЗАГРУЗКА ФАЙЛА ИНИЦИАЛИЗАЦИИ

;Файл mycall.ini хранит состояние списков и положение главного окна на момент

;завершения предыдущего сеанса работы приложения

;///////////////////////////////////////////////////////////// Загрузка файла инициализации

.data?

ini_filedd ?

buffer db INI_FILE_LENGTH dup(?)

bytes_readwrite dd ?

.const

ini_file_name db "mycall.ini",0

.code

load_ini PROC

;Инициализация глобальных переменных

mov main_win_left,MAIN_WIN_DEFAULT_LEFT

mov main_win_top,MAIN_WIN_DEFAULT_TOP

mov current_con,0

mov ebx,MAX_CON

@while(ebx)

dec ebx

mov [con_phone+ebx],0

mov [con_user+ebx],0

@endw

;Попытка загрузки файла mycall.ini

;В случае неудачи используются значения по умолчанию

invoke CreateFileA,offset ini_file_name,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL

@if(eax!=INVALID_HANDLE_VALUE)

;Чтение файла mycall.ini

push eax

invoke ReadFile,eax,offset buffer,INI_FILE_LENGTH,offset bytes_readwrite,NULL

@if(eax)

;Приведение позиции окна к фактическим размерам экрана и запоминание



xor edx,edx

mov dx,[word ptr buffer]

push edx

invoke GetSystemMetrics,SM_CXSCREEN

sub eax,10

pop edx

@if(edx<=eax)

mov main_win_left,edx

@endif

xor edx,edx

mov dx,[word ptr buffer+2]

push edx

invoke GetSystemMetrics,SM_CYSCREEN

sub eax,10

pop edx

@if(edx<=eax)

mov main_win_top,edx

@endif

;Запоминание позиций списков телефонов и логинов

xor edx,edx

mov dl,[byte ptr buffer+4]

mov current_con,edx

xor ecx,ecx

xor ebx,ebx

@while(ecx<MAX_CON)

mov ax,[word ptr buffer+5+ebx]

mov [con_phone+ecx],al

mov [con_user+ecx],ah

add ebx,2

inc ecx

@endw

@endif

pop eax

invoke CloseHandle,eax

@endif

ret

load_ini ENDP

;СОХРАНЕНИЕ ФАЙЛА ИНИЦИАЛИЗАЦИИ mycall.ini

; В файле сохраняются положение главного окна на экране и позиции

;списков на момент завершения работы приложения

;============================================================= Сохранение файла инициализации

save_ini PROC

invoke CreateFileA,offset ini_file_name,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL

@if(eax!=INVALID_HANDLE_VALUE)

push eax

mov eax,main_win_left

mov [word ptr buffer],ax

mov eax,main_win_top

mov [word ptr buffer+2],ax

mov eax,current_con

mov [buffer+4],al

xor ecx,ecx

xor ebx,ebx

@while(ecx<MAX_CON)

mov al,[con_phone+ecx]

mov ah,[con_user+ecx]

mov [word ptr buffer+5+ebx],ax

add ebx,2

inc ecx

@endw

pop eax

push eax

invoke WriteFile,eax,offset buffer,INI_FILE_LENGTH,offset bytes_readwrite,NULL

pop eax

invoke CloseHandle,eax

@endif

ret

save_ini ENDP

;ЗАГРУЗКА ФАЙЛА ДАННЫХ mycall.dat

;Файл содержит имена соединений, телефоны и логины

;///////////////////////////////////////////////////////////// Загрузка файла данных

.data?

dat_file dd ?

dat_file_size dd ?

.const

dat_file_name db "mycall.txt",0

.code

load_data PROC

;Открытие файла, подготовка буфера для него и считывание файла в буфер

invoke CreateFileA,offset dat_file_name,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL

@if(eax==INVALID_HANDLE_VALUE)



mov eax,FATAL_DAT_FILE_OPEN

jmp ld_out

@endif

mov dat_file,eax

invoke GetFileSize,eax,NULL

@if(eax==0ffffffffh)

mov eax,FATAL_DAT_FILE_SIZE

jmp ld_out

@endif

mov dat_file_size,eax

add eax,3

invoke GlobalAlloc,GMEM_FIXED,eax

@if(!eax)

mov eax,FATAL_DAT_BUF_ALLOC

jmp ld_out

@endif

mov dat_buffer,eax

invoke ReadFile,dat_file,eax,dat_file_size,offset bytes_readwrite,NULL

@if(!eax)

mov eax,FATAL_DAT_FILE_READ

jmp ld_out

@endif

;Замена слэшей, CR, LF и пробелов на 0h, добавление в конец 1h

xor ebx,ebx

mov esi,dat_buffer

@while(ebx<dat_file_size)

mov al,byte ptr [esi][ebx]

xor edx,edx

@if(al==0dh)

inc edx

@elseif(al==0ah)

inc edx

@elseif(al==' ')

inc edx

@elseif(al=='/')

inc edx

@endif

@if(edx)

mov byte ptr [esi][ebx],0

@endif

inc ebx

@endw

mov word ptr [esi][ebx],0

add ebx,2

mov byte ptr [esi][ebx],1

xor eax,eax

ld_out:

push eax

invoke CloseHandle,dat_file

pop eax

ret

load_data ENDP

;ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ

;Используются при разборе содержимого буфера данных:

;skip_nz - пропуск всех символов, пока не встретится 00h

;skip_nz2 - пропуск всех символов, пока не встретится 0000h

;///////////////////////////////////////////////////////////// Пропуски

skip_nz PROC

@while(byte ptr[esi]!=0)

inc esi

@endw

nc esi

ret

skip_nz ENDP

;=============================================================

skip_nz2 PROC

@while(word ptr[esi]!=0)

inc esi

@endw

add esi,2

ret

skip_nz2 ENDP

;ИЗМЕНЕНИЕ СОЕДИНЕНИЯ

;Функция вызывается при выборе в списке соединений нового соединения

;///////////////////////////////////////////////////////////// Изменение соединения

change_con PROC

;Установка указателя на запись текущего соединения

xor edx,edx ;sub_string

xor ecx,ecx ;cur_con

mov esi,dat_buffer

@while(byte ptr[esi]!=1)

@if(ecx==current_con)

@break

@endif

@if(word ptr[esi]==0)

inc edx

@if(edx==3)

inc ecx

xor edx,edx

@endif

inc esi

@endif

inc esi

@endw

call skip_nz2

;Формирование списка телефонов для данного соединения и установка его позиции



push esi

invoke SendMessageA,phon_window,CB_RESETCONTENT,0,0

pop esi

xor edx,edx;number

@while(byte ptr[esi]!=0)

@push esi,edx

invoke SendMessageA,phon_window,CB_ADDSTRING,0,esi

@pop esi,edx

inc edx

call skip_nz

@endw

inc esi

mov ebx,current_con

@if(con_phone[ebx]>=dl)

mov byte ptr con_phone[ebx],0

@endif

xor eax,eax

mov al,con_phone[ebx]

invoke SendMessageA,phon_window,CB_SETCURSEL,eax,esi

; Формирование списка логинов для данного соединения и установка его позиции

invoke SendMessageA,user_window,CB_RESETCONTENT,0,0

xor edx,edx

@while(byte ptr[esi]!=0)

@push esi,edx

invoke SendMessageA,user_window,CB_ADDSTRING,0,esi

@pop esi,edx

inc edx

call skip_nz

call skip_nz

@endw

mov ebx,current_con

@if(con_user[ebx]>=dl)

mov byte ptr con_user[ebx],0

@endif

xor eax,eax

mov al,con_user[ebx]

invoke SendMessageA,user_window,CB_SETCURSEL,eax,esi

ret

change_con ENDP

;АВАРИЙНОЕ ЗАВЕРШЕНИЕ ПРИЛОЖЕНИЯ

;Вызывается в случае, когда продолжение работы приложения невозможно

;///////////////////////////////////////////////////////////// Фатальный аборт

.const

fatal_caption db "MyCall Error",0

fatal_txt db "Can't register main window class",0

db "Can't create main window",0

db "Can't open mycall.txt",0

db "Can't get size of mycall.txt",0

db "Can't allocate memory for mycall.txt",0

db "Can't read mycall.txt",0

db "Remote Access Service fatal error",0

.code

fatal PROC fatal_code

mov esi,offset fatal_txt

@while(fatal_code)

@while(byte ptr [esi])

inc esi

@endw

inc esi

dec fatal_code

@endw

invoke MessageBoxA,NULL,esi,offset fatal_caption,MB_OK OR MB_ICONERROR

ret

fatal ENDP

;В(Ы)КЛЮЧЕНИЕ ОРГАНОВ УПРАВЛЕНИЯ

;Меняет надпись на кнопке и активизирует списки в зависимости от того,

;находится приложение в режиме выбора или в режиме соединения

;///////////////////////////////////////////////////////////// Выключение органов управления

.const

ec_txt0 db "Call",0



ec_txt1 db "HangUp",0

.code

disable_controls PROC disable

mov eax,win_height

@if(disable)

add eax,12

@endif

invoke SetWindowPos,main_window,NULL,NULL,NULL,win_width,eax,SWP_NOMOVE OR SWP_NOZORDER

@if(disable)

mov eax,offset ec_txt1

@else

mov eax,offset ec_txt0

@endif

invoke SendMessageA,butt_window,WM_SETTEXT,0,eax

mov eax,disable

xor eax,1h

push eax

invoke EnableWindow,conn_window,eax

pop eax

push eax

invoke EnableWindow,phon_window,eax

pop eax

invoke EnableWindow,user_window,eax

ret

disable_controls ENDP

;ДОЗВОН

;///////////////////////////////////////////////////////////// Дозвон

.data?

ras_dial_params RASDIALPARAMS{}

.code

ras_dial PROC

;Подготовка структуры RASDIALPARAMS

mov ras_dial_params.dwSize,sizeof(RASDIALPARAMS)

mov ras_dial_params.szCallbackNumber,0

mov ras_dial_params.szDomain,0

mov esi,dat_buffer

xor ebx,ebx

@while(ebx!=current_con)

xor ecx,ecx

@while(ecx<3)

call skip_nz2

inc ecx

@endw

inc ebx

@endw

mov ras_dial_params.szEntryName,0

push esi

invoke lstrcpy,offset ras_dial_params.szEntryName,esi

pop esi

call skip_nz

mov ras_dial_params.szPhoneNumber,0

push esi

invoke lstrcpy,offset ras_dial_params.szPhoneNumber,esi

pop esi

call skip_nz

dec esi

@while(byte ptr[esi]==0)

inc esi

@endw

xor ecx,ecx

mov ebx,current_con

@while(con_phone[ebx]!=cl)

call skip_nz

inc ecx

@endw

push esi

invoke lstrcat,offset ras_dial_params.szPhoneNumber,esi

pop esi

call skip_nz2

xor ecx,ecx

mov ebx,current_con

@while(con_user[ebx]!=cl)

call skip_nz

call skip_nz

inc ecx

@endw

mov ras_dial_params.szUserName,0

push esi

invoke lstrcat,offset ras_dial_params.szUserName,esi

pop esi

call skip_nz

mov ras_dial_params.szPassword,0

push esi

invoke lstrcat,offset ras_dial_params.szPassword,esi

pop esi

;Дозвон

mov ras_conn,0

invoke RasDialA,0,0,offset ras_dial_params,0,ras_dial_func,offset ras_conn

@if(eax)

call ras_hangup

invoke fatal,FATAL_RAS

invoke ExitProcess,EXIT_RAS_ERROR

@else

mov online,TRUE



@endif

ret

ras_dial ENDP

;ФУНКЦИЯ КОНТРОЛЯ СОСТОЯНИЯ СОЕДИНЕНИЯ

;============================================================= RAS Callback function

.const

ras_state_text db "OpenPort",0

db "PortOpened",0

db "ConnectDevice",0

db "DeviceConnected",0

db "AllDevicesConnected",0

db "Authenticate",0

db "AuthNotify",0

db "AuthRetry",0

db "AuthCallback",0

db "AuthChangePassword",0

db "AuthProject",0

db "AuthLinkSpeed",0

db "AuthAck",0

db "ReAuthenticate",0

db "Authenticated",0

db "PrepareForCallback",0

db "WaitForModemReset",0

db "WaitForCallback",0

db "Projected",0

db "StartAuthentication",0

db "CallbackComplete",0

db "LogonNetwork",0

db "SubEntryConnected",0

db "SubEntryDisconnected",0

db "Interactive",0

db "RetryAuthentication",0

db "CallbackSetByCaller",0

db "PasswordExpired",0

db "Connected",0

db "Disconnected",0

db "Unknown state",0

.data?

ras_monitor_id dd ?

.code

ras_dial_func PROC PUBLIC type_of_event,rasconnstate,ras_error

push esi

@if(online)

;Показ состояния соединения в строке статуса

mov edx,rasconnstate

@if(edx>=RASCS_Connected)

sub edx,RASCS_Connected-28

@else

@if(edx>=RASCS_Interactive)

sub edx,RASCS_Interactive-24

@endif

@endif

@if(edx>30)

mov edx,30

@endif

push edx

mov esi,offset ras_state_text

@while(edx)

call skip_nz

dec edx

@endw

invoke SendMessageA,stat_window,WM_SETTEXT,0,esi

pop edx

;Если соединение установлено - запуск нити контроля

@if(rasconnstate==RASCS_Connected)

invoke CreateThread,NULL,0,offset ras_monitor_func,0,0,offset ras_monitor_id

; Если соединение не установлено - передача сообщения WM_USER для

;повтора дозвона

@elseif(rasconnstate==RASCS_Disconnected)



push edx

invoke PostMessageA,main_window,WM_USER,0,0

pop edx

@endif

@endif

pop esi

ret

ras_dial_func ENDP

;НИТЬ МОНИТОРА РАЗРЫВА

; Каждые 200 мс опрашивает состояние установленного соединения.

;При обнаружении факта разъединения посылает сообщение WM_USER

;и завершается

;============================================================= Нить монитора разрыва

.data?

ras_connect_status RASCONNSTATUS{}

.code

ras_monitor_func PROC PUBLIC param

rm_loop:

@if(online)

mov ras_connect_status.dwSize,sizeof(RASCONNSTATUS)

invoke RasGetConnectStatusA,ras_conn,offset ras_connect_status

@if(!eax)

@if(ras_connect_status.rasconnstate!=RASCS_Disconnected)

invoke Sleep,200

jmp rm_loop

@endif

@endif

@endif

invoke PostMessageA,main_window,WM_USER,0,1

ret

ras_monitor_func ENDP

;ПОЛОЖИТЬ ТРУБКУ

;Если происходит дозвон, или соединение установлено,

;дает команду на разрыв и ожидает, когда RAS ее выполнит

;///////////////////////////////////////////////////////////// Положить трубку

ras_hangup PROC

mov online,FALSE

@if(ras_conn)

invoke RasHangUpA,ras_conn

rh_loop:

mov ras_connect_status.dwSize,sizeof(RASCONNSTATUS)

invoke RasGetConnectStatusA,ras_conn,offset ras_connect_status

@if(eax!=ERROR_INVALID_HANDLE)

invoke Sleep,0

jmp rh_loop

@endif

@endif

mov ras_conn,0

invoke Sleep,1000

ret

ras_hangup ENDP

br>

;#############################################################

end


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