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

Исполнимые программы для Windows делятся на два основных типа - кон­сольные и графические приложения. При запуске консольного приложения от-окно, с которым программа может общаться функциями

WriteConsole/ReadConsole и другими (соответственно при запуске из другого

консольного приложения, например файлового менеджера FAR, программе отво­дится текущая консоль и управление не возвращается к FAR, пока программа не закончится). Графические приложения соответственно не получают консоли и дол­жны открывать окна, чтобы вывести что-нибудь на экран.

Для компиляции консольных приложений мы будем пользоваться следующи­ми командами.

MASM:

ml /с /coff /Ср winurl.asm

link winurl.asm /subsystem:console

TASM:

tasm /т /ml /D_TASM_ winurl.asm tlink32 /Тре /ар /с /х winurl.obj

WASM:

wasm winurl.asm

wlink file winurl.obj form windows nt runtime console op с

Попробуйте скомпилировать программу winurl.asm указанным способом, что­бы увидеть, как отличается работа консольного приложения от графического.

В качестве примера полноценного консольного приложения напишем программу, которая перечислит все подключенные сетевые ресурсы (диски и принтеры), исполь­зуя системные функции и

; netenum.asm

;  Консольное приложение для Win32,  перечисляющее сетевые ресурсы. include def32.inc

include

. inc

 

include mpr.inc

 

 

.386

 

 

.model

flat

 

.const

 

 

greet jnessage

db

'Example Win32 console program' ,ODh,OAh,ODh,OAh,0

errorljnessage

db

ODh.OAh, 'Could not get current user name' ,ODh,OAh,0

error2_message

db

ODh,OAh, 'Could not enumerate' ,ODh,OAh,0

good_exit_msg

db

ODh,OAh,ODh,OAh, 'Normal termination' ,ODh,OAh,0

enum_msg1

db

ODh, OAh, '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

7                      ; Переменная для WriteConsole.

enum_handle

dd

?                     ; Идентификатор для WNetEnumResource.

.code

_start:

;   Получить от системы идентификатор буфера вывода stdout.

push STD_OUTPUT_HANDLE

call GetStdHandle                   ; Возвращает идентификатор STDOUT в еах,

mov а мы будем хранить его в ЕВХ. ;   Вывести строку greetjnessage на экран.

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,N0_ERR0R ;  Если произошла ошибка,

jne .  error_exitl ; выйти,

mov esi, offset user name ;  иначе - вывести строку на экран.

. call output_string

;  Начать перечисление сетевых ресурсов.

push offset enum handle ;   Идентификатор для WNetEnumResource.

push О

push RESOURCEUSAGE_CONNECTABLE ;   Все присоединяемые ресурсы.

push RESOURCETYPE_ANY ;' Ресурсы любого типа.

push RESOURCE_CONNECTEO ;  Толвко присоединенные сейчас.

call ' WNetOpenEnum ;  Начаты перечисление.

cmp eax.NO ERROR ;  Если произошла ошибка,

jne error_exit2 ; выйти. ; Цикл перечисления ресурсов. enumer-ation_loop:

push offset enum_buf_l ; Длина буфера в байтах.

push offset -enum_buf ; Адрес буфера.

push offset enum entries ; Число ресурсов.

push dword ptr enumjiandle ; Идентификатор от WNetOpenEnum.

call WNetEnumResource

cmp eax,ERROR_NO_MORE_ITEMS ; Если они закончилисв,

je end_enumeration : завершитв перечисление,

cmp eax,NO_ERROR ; если произошла ошибка,

jne error_exit2 : выйти с сообщением об ошибке. I Вывод информации о ресурсе на экран.

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 ; Продолжитв перечисление. I Конец цикла. 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 exitl: программы.

mov jmp

error_exit2: mov

jmp

esi,offset error1_message

short

esi,offset error2_message

short

; Процедрура output_string. ; Выводит на экран строку. ;  Вход:  esi - адрес строки.

; ebx - идентификатор stdout или другого консольного буфера.

proc near ; Определить длину строки.

eld

 

xor

eax,eax

mov

edi,esi

repne

scasb

dec

edi

sub

edi,esi

h ee Ha

консоль.

push

0

push

offset message ]

push

edi

push

esi

push

ebx

call .

WriteConsole

ret

 

Сколько байтов выведено на консоль. Сколько байтов надо вывести на консоль. Адрес строки для вывода на консоль. Идентификатор буфера вывода. ІМгібеСопзоІе   (пСоггзо1еОЧЛ:рігІ, ІруВі^гег, сспТоИг^е, ІрссґМгШеп, ІругІеБегуей)

output_string endp end _start

В файл kernel32.inc надо добавить между ifdef _TASM_ и else строки:

GetStdHandle:near

WriteConsoleAmear

WriteConsoleA

__imp__GetStdHandle@4 _imp_WriteConsoleA@20 „imp__GetStdHandle@4 _imp_WriteConsoleA@20

extrn extrn

WriteConsole equ и между- else и endif:

extrn

extrn

GetStdHandle equ WriteConsole equ

Кроме того, надо создать файл mpr.inc: ; mpr.inc

;  Включаемый файл с определениями функций из mpr.dll.

ifdef _TASM_

includelib import32.lib

; Имена используемых функций. ■

extrn WNetGetUserAmear extrn WNetOpenEnumAmear

extrn     WNetEnumResourceA:near extrn WNetCloseEnum:near

;  Присваивания для облегчения читаемости кода. WNetGetUser        equ WNetGetUserA WNetOpenEnum       equ WNetOpenEnumA WNetEnumResource equ WNetEnumResourceA

else

indudelib mpr.lib ; Истинные имена используемых функций.

extrn_imp_WNetGetUserA@12: dword ■

extrn_imp_WNet0penEnumA@20: dword

extrn_imp_WNetEnumResourceA(5>16:dword -

extrn_imp_WNetGloseEnum@4: dword

;  Присваивания для облегчения читаемости кода. WNetGetUser equ __imp__WNetGetUserA(s>12

WNetOpenEnum       equ_imp_WNet0penEnumA@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

2

; Из 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

Этот пример, разумеется, можно было построить эффективнее, выделив боль­шой буфер для WNetEnumResouгce, например при помощи LocalAlloc илприложения

GlobalAlloc (в Win32 это одно и то же), а потом, прочитав информацию обо всех ресурсах, хранящихся в нем, пришлось бы следить за тем, кончились они или нет, и вызывать WNetEnumResource еще раз.