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

7.2. Консольные приложения

Исполнимые программы для 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() еще раз.


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