| [Назад] [Далее] | ![]() |
Исполнимые программы для Windows делятся на два основных типа — консольные и графические приложения. При запуске консольного приложения открывается текстовое окно, с которым программа может общаться функциями WriteConsole()/ReadConsole() и другими (соответственно при запуске из другого консольного приложения, например, файлового менеджера FAR, программе отводится текущая консоль и управление не возвращается к FAR, пока программа не закончится). Графические приложения соответственно не получают консоли и должны открывать окна, чтобы вывести что-нибудь на экран.
Для компиляции консольных приложений мы будем пользоваться следующими командами:
MASM:
ml /с /coff /Cp winurl.asm link winurl.asm /subsystem console
TASM:
tasm /m /ml /D_TASM_ winurl.asm tlink32 /Тре /ар /с /x winurl.obj
WASM:
wasm winurl.asm wlink file winurl.obj form windows nt runtime console op с
Попробуйте скомпилировать программу winurl.asm этим способом, чтобы увидеть, как отличается работа консольного приложения от графического.
В качестве примера полноценного консольного приложения напишем программу, которая перечислит все подключенные сетевые ресурсы (диски и принтеры), используя системные функции WNetOpenEnum(), WNetEnumResource() и WNetCloseEnum().
; netenum.asm
; Консольное приложение для win32, перечисляющее сетевые ресурсы
include def32.inc
include kernel32.inc
include mpr.inc
.386
.model flat
.const
greet_message db 'Example win32 console program',0Dh,0Ah,0Dh,0Ah,0
error1_message db 0Dh,0Ah,'Could not get current user name',0Dh,0Ah,0
error2_message db 0Dh,0Ah,'Could not enumerate',0Dh,0Ah,0
good_exit_msg db 0Dh,0Ah,0Dh,0Ah,'Normal termination',0Dh,0Ah,0
enum_msg1 db 0Dh,0Ah,'Local ',0
enum_msg2 db ' remote - ',0
.data
user_name db 'List of connected resources for user '
user_buff db 64 dup (?) ; буфер для WNetGetUser
user_buff_l dd $-user_buff ; размер буфера для WNetGetUser
enum_buf_l dd 1056 ; длина enum_buf в байтах
enum_entries dd 1 ; число ресурсов, которые в нем помещаются
.data?
enum_buf NTRESOURCE <?,?,?,?,?,?,?,?> ; буфер для WNetEnumResource
dd 256 dup (?) ; 1024 байт для строк
message_l dd ? ; переменная для WriteConsole
enum_handle dd ? ; идентификатор для WNetEnumResource
.code
_start:
; получим от системы идентификатор буфера вывода stdout
push STD_OUTPUT_HANDLE
call GetStdHandle ; возвращает идентификатор STDOUT в eax
mov ebx,eax ; а мы будем хранить его в EBX
; выведем строку greet_message на экран
mov esi,offset greet_message
call output_string
; определим имя пользователя, которому принадлежит наш процесс
mov esi,offset user_buff
push offset user_buff_l ; адрес переменной с длиной буфера
push esi ; адрес буфера
push 0 ; NULL
call WNetGetUser
cmp eax,NO_ERROR ; если произошла ошибка
jne error_exit1 ; выйти
mov esi,offset user_name ; иначе - выведем строку на экран
call output_string
; начнем перечисление сетевых ресурсов
push offset enum_handle ; идентификатор для WNetEnumResource
push 0
push RESOURCEUSAGE_CONNECTABLE ; все присоединяемые ресурсы
push RESOURCETYPE_ANY ; ресурсы любого типа
push RESOURCE_CONNECTED ; только присоединенные сейчас
call WNetOpenEnum ; начать перечисление
cmp eax,NO_ERROR ; если произошла ошибка
jne error_exit2 ; выйти
; цикл перечисления ресурсов
enumeration_loop:
push offset enum_buf_l ; длина буфера в байтах
push offset enum_buf ; адрес буфера
push offset enum_entries ; число ресурсов
push dword ptr enum_handle ; идентификатор от WNetOpenEnum
call WNetEnumResource
cmp eax,ERROR_NO_MORE_ITEMS ; если они закончились
je end_enumeration ; завершить перечисление
cmp eax,NO_ERROR ; если произошла ошибка
jne error_exit2 ; выйти с сообщением об ошибке
; вывод информации ресурсе на экран
mov esi,offset enum_msg1 ; первая часть строки
call output_string ; на консоль
mov esi,dword ptr enum_buf.lpLocalName ; локальное имя устройства
call output_string ; на консоль
mov esi,offset enum_msg2 ; вторая часть строки
call output_string ; на консоль
mov esi,dword ptr enum_buf.lpRemoteName ; удаленное имя устройства
call output_string ; туда же
jmp short enumeration_loop ; продолжим перечисление
; конец цикла
end_enumeration:
push dword ptr enum_handle
call WNetCloseEnum ; конец перечисления
mov esi,offset good_exit_msg
exit_program:
call output_string ; выведем строку
push 0 ; код выхода
call ExitProcess ; конец программы
; выходы после ошибок
error_exit1:
mov esi,offset error1_message
jmp short exit_program
error_exit2:
mov esi,offset error2_message
jmp short exit_program
; процедрура output_string
; выводит на экран строку
; ввод: esi - адрес строки
; ebx - идентификатор stdout или другого консольного буфера
output_string proc near
; определим длину строки
cld
xor eax,eax
mov edi,esi
repne scasb
dec edi
sub edi,esi
; пошлем ее на консоль
push 0
push offset message_l ; сколько байт выведено на консоль
push edi ; сколько байт надо вывести на консоль
push esi ; адрес строки для вывода на консоль
push ebx ; идентификатор буфера вывода
call WriteConsole ; WriteConsole(hConsoleOutput,lpvBuffer,cchToWrite,
; lpcchWritten,lpvReserved)
ret
output_string endp
end _start
В файл kernel32.inc надо добавить между ifdef _TASM_ и else строки:
extrn GetStdHandle:near
extrn WriteConsoleA:near
WriteConsole equ WriteConsoleA
и между else и endif:
extrn __imp__GetStdHandle@4:dword
extrn __imp__WriteConsoleA@20:dword
GetStdHandle equ __imp__GetStdHandle@4
WriteConsole equ __imp__WriteConsoleA@20
Кроме того, надо создать файл mpr.inc:
; mpr.inc
; включаемый файл с определениями функций из mpr.dll
;
ifdef _TASM_
includelib import32.lib
; имена используемых функций
extrn WNetGetUserA:near
extrn WNetOpenEnumA:near
extrn WNetEnumResourceA:near
extrn WNetCloseEnum:near
; присваивания для облегчения читаемости кода
WNetGetUser equ WNetGetUserA
WNetOpenEnum equ WNetOpenEnumA
WNetEnumResource equ WNetEnumResourceA
else
includelib mpr.lib
; истинные имена используемых функций
extrn __imp__WNetGetUserA@12:dword
extrn __imp__WNetOpenEnumA@20:dword
extrn __imp__WNetEnumResourceA@16:dword
extrn __imp__WNetCloseEnum@4:dword
; присваивания для облегчения читаемости кода
WNetGetUser equ __imp__WNetGetUserA@12
WNetOpenEnum equ __imp__WNetOpenEnumA@20
WNetEnumResource equ __imp__WNetEnumResourceA@16
WNetCloseEnum equ __imp__WNetCloseEnum@4
endif
Еще потребуется файл def32.inc, в который поместим определения констант и структур из разных включаемых файлов для языка С. Существует утилита h2inc, преобразующая эти файлы целиком, но мы создадим собственный включаемый файл, в который будем добавлять новые определения по мере надобности.
; def32.inc
; файл с определениями констант и типов для примеров программ под win32
; из winbase.h
STD_OUTPUT_HANDLE equ -11
; из winerror.h
NO_ERROR equ 0
ERROR_NO_MORE_ITEMS equ 259
; из winnetwk.h
RESOURCEUSAGE_CONNECTABLE equ 1
RESOURCETYPE_ANY equ 0
RESOURCE_CONNECTED equ 1
NTRESOURCE struc
dwScope dd ?
dwType dd ?
dwDisplayType dd ?
dwUsage dd ?
lpLocalName dd ?
lpRemoteName dd ?
lpComment dd ?
lpProvider dd ?
NTRESOURCE ends
Ётот пример, разумеется, можно было построить более эффективно, выделив большой буфер для WNetEnumResource(), например при помощи LocalAlloc() или GlobalAlloc() (в Win32 это одно и то же), и затем, прочитав информацию обо всех ресурсах из него, пришлось бы следить за тем, кончились ресурсы или нет, и вызывать WNetEnumResource() еще раз.