Шаг 78.
Рекурсивный поиск сетевых ресурсов

    На этом шаге мы рассмотрим организацию поиска сетевых ресурсов.

    Следующая программа осуществляет рекурсивный поиск сетевых ресурсов. Работая в консольном режиме, она выдает на экран название провайдера и удаленное имя ресурса. Данная программа должна правильно работать и в сетях Microsoft, и в сетях Novell.

.386P
;Плоская модель.
.MODEL FLAT, STDCALL
;Константы.
STD_OUTPUT_HANDLE  equ -11
RESOURCETYPE_DISK  equ 1h
RESOURCE_GLOBALNET equ 2h
RESOURCETYPE_ANY   equ 0h
;Прототипы внешних процедур.
IFDEF MASM
    EXTERN  CharToOemA@8:NEAR
    EXTERN  RtlMoveMemory@12:NEAR
    EXTERN  WNetCloseEnum@4:NEAR
    EXTERN  WNetEnumResourceA@16:NEAR
    EXTERN  WNetOpenEnumA@20:NEAR
    EXTERN  lstrcpy@8:NEAR
    EXTERN  lstrcat@8:NEAR
    EXTERN  lstrlen@4:NEAR
    EXTERN  GetStdHandle@4:NEAR
    EXTERN  WriteConsoleA@20:NEAR
    EXTERN  ExitProcess@4:NEAR
    EXTERN  GetCommandLineA@0:NEAR
ELSE
    EXTERN  CharToOemA:NEAR 
    EXTERN  RtlMoveMemory:NEAR 
    EXTERN  WNetCloseEnum:NEAR 
    EXTERN  WNetEnumResourceA:NEAR 
    EXTERN  WNetOpenEnumA:NEAR 
    EXTERN  lstrcpy:NEAR 
    EXTERN  lstrcat:NEAR 
    EXTERN  lstrlen:NEAR 
    EXTERN  GetStdHandle:NEAR 
    EXTERN  WriteConsoleA:NEAR 
    EXTERN  ExitProcess:NEAR 
    EXTERN  GetCommandLineA:NEAR 
    CharToOemA@8         = CharToOemA 
    RtlMoveMemory@12     = RtlMoveMemory 
    WNetCloseEnum@4      = WNetCloseEnum 
    WNetEnumResourceA@16 = WNetEnumResourceA 
    lstrcpy@8            = lstrcpy 
    WNetOpenEnumA@20     = WNetOpenEnumA 
    lstrcat@8            = lstrcat 
    lstrlen@4            = lstrlen 
    GetStdHandle@4       = GetStdHandle 
    WriteConsoleA@20     = WriteConsoleA 
    ExitProcess@4        = ExitProcess 
    GetCommandLineA@0    = GetCommandLineA
ENDIF
;Структуры.
NETRESOURCE  STRUC
             dwScope       DWORD  ?
             dwType        DWORD  ?
             dwDisplayType DWORD  ?
             dwUsage       DWORD  ?
             lpLocalName   DWORD  ?
             lpRemoteName  DWORD  ?
             lpCorament    DWORD  ?
             lpProvider    DWORD  ? 
NETRESOURCE ENDS

;Директивы компоновщику для подключения библиотек.
IFDEF MASM
;Для компоновщика  LINK.EXE.
    includelib \masm32\lib\user32.lib
    includelib \masm32\lib\kernel32.lib
    includelib \masm32\lib\mpr.lib
ELSE
;Для компоновщика  TLINK32.EXE.
    includelib c:\tasm32\lib\import32.lib
ENDIF
;------------------------------------------------
;Сегмент данных. 
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
     LENS       DWORD  ? ;Количество выведенных символов.
     HANDL      DWORD  ? 
     NR         NETRESOURCE <?>
     BUF        DB 100 DUP(0)
     ENT        DB 13,10,0
_DATA ENDS 
;Сегмент кода.
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START: 
;Получить дескриптор вывода.
     PUSH STD_OUTPUT_HANDLE
     CALL GetStdHandle@4
     MOV  HANDL,EAX
;Запустить процедуру поиска сетевых ресурсов.
     PUSH 0
     PUSH OFFSET NR
     CALL POISK 
_END:
     PUSH 0
     CALL ExitProcess@4 
;Процедура поиска.
     PAR1 EQU [EBP+8]   ;Указатель на структуру. 
     PAR2 EQU [EBP+0CH] ;Признак. 
;Локальные переменные.
     HANDLP EQU   [EBP-4]     ;Дескриптор поиска.
     CC     EQU   [EBP-8]
     NB     EQU   [EBP-12]
     NR1    EQU   [EBP-44]    ;Структура.
     BUFER  EQU   [EBP-144]   ;Буфер.
     RS     EQU   [EBP-32144] ;Массив  структур.
POISK PROC
     PUSH EBP 
     MOV  EBP,ESP 
     SUB  ESP,32144 
     CMP  DWORD PTR PAR2,0 
     JNE  SECOND
;При первом запуске NULL. 
     XOR  EBX,EBX 
     JMP  FIRST 
SECOND:
;Запуск при рекурсивном вызове. 
;Вначале скопировать  структуру в локальную 
;переменную, хотя для данной программы это излишне. 
     PUSH 32
     PUSH DWORD PTR PAR1 
     LEA  EAX,DWORD PTR NR1 
     PUSH EAX
     CALL RtlMoveMemory@12 
;При вторичном поиске указатель  на структуру.
     LEA  EBX,DWORD PTR NR1 
FIRST:
;Запуск при первом вызове. 
     LEA  EAX,HANDLP 
     PUSH EAX 
     PUSH EBX 
     PUSH 0
     PUSH RESOURCETYPE_ANY 
     PUSH RESOURCE_GLOBALNET 
     CALL WNetOpenEnumA@20 
     CMP  EAX,0 
     JNE  _EN
;Здесь осуществляется основной поиск. 
REP1:
;Запуск функции WNetEnumResource. 
;Объем массива структур NETRESOURCE.
     MOV  DWORD PTR NB,32000
     LEA  EAX,NB
     PUSH EAX
     LEA  EAX,RS
     PUSH EAX 
;Искать максимальное количество объектов.
     MOV  DWORD PTR CC,0FFFFFFFFH
     LEA  EAX,CC
     PUSH EAX
     PUSH DWORD PTR HANDLP
     CALL WNetEnumResourceA@16
     CMP  EAX,0
     JNE  _CLOSE 
;Цикл по полученному массиву.
     MOV  ESI,CC
     SHL  ESI,5 ;Умножаем на 32.
     MOV  EDI,0 
LOO:
     CMP  EDI,ESI
     JE   REP1
;Вывод информации. 
;Провайдер.
     MOV  EBX,DWORD PTR RS[EDI]+28
     CALL SETMSG 
;Удаленное имя.
     MOV  EBX,DWORD PTR RS[EDI]+20
     CALL SETMSG 
;Сохранить нужные регистры.
     PUSH ESI
     PUSH EDI 
;Теперь рекурсивный вызов.
     PUSH 1
     LEA  EAX,DWORD PTR RS[EDI]
     PUSH EAX
     CALL POISK 
;Восстановить регистры.
     POP  EDI
     POP  ESI 
     ADD  EDI,32 
     JMP  LOO
;------------------------------------------------
     JMP  REP1
;------------------------------------------------
_CLOSE:
     PUSH DWORD PTR HANDLP 
     CALL WNetCloseEnum@4 
_EN:
     MOV  ESP,EBP 
     POP  EBP 
     RET  8 
POISK ENDP 
;Вывод сообщения.
;EBX -> строка.
SETMSG PROC
;Скопировать текст в отдельный буфер. 
    PUSH EBX 
    PUSH OFFSET BUF 
    CALL lstrcpy@8 
    LEA  EBX,BUF
;Перекодировать для консоли. 
    PUSH EBX 
    PUSH EBX
    CALL CharToOemA@8 
;Добавить  перевод строки 
    PUSH OFFSET ENT 
    PUSH EBX 
    CALL lstrcat@8 
;Определить длину строки. 
    PUSH EBX 
    CALL lstrlen@4 
;Вывести строку. 
    PUSH 0
    PUSH OFFSET LENS 
    PUSH EAX 
    PUSH EBX 
    PUSH HANDL
    CALL WriteConsoleA@20
    RET
SETMSG ENDP 
_TEXT ENDS 
      END START
Текст этого файла можно взять здесь.

    Трансляция программы:

    Приведенная программа довольно сложна и требует серьезных пояснений. Прежде всего отметим, что, если читатель действительно хочет разобраться в сетевом программировании (в данном случае мы говорим о локальной сети), ему необходимо самостоятельно написать несколько программ. Приведенные программы могут служить отправными точками.

  1. Нам уже приходилось сталкиваться с локальными переменными, когда мы рассматривали поиск файлов по дереву каталогов. Данная задача весьма похожа, но есть и отличие. В данном случае мы используем слишком большой объем для локальных переменных. По этой причине мы явно указываем (заказываем) большой объем стека (опции STACK и S, Sc). По умолчанию компоновщик устанавливает всего 8 Кбайт, что явно не достаточно.

  2. Функция WNetEnumResource требует указать своим параметром массив структур NETRESOURCE. Объем одной структуры 32 байта. Мы резервируем тысячу таких структур. Не много ли, скажете вы? Честно говоря, мы не встречали локальной сети с тысячью сетевых компьютеров. Однако бывают локальные сети, где на одном из серверов было создано около восьмисот сетевых каталогов. Если говорить на чистоту, то здесь мы все же демонстрируем не лучший стиль программирования. Более корректный путь заключается в том, что функция WNetEnumResource вначале вызывается с указанием объема буфера меньше, чем 32 байта, - в этом случае в переменную, содержащую объем буфера, будет возвращен необходимый объем. Зная необходимый объем, программа должна запросить у системы нужный и вторично запустить WNetEnumResource. Данный подход более корректен, но более сложен.

  3. При рекурсивном вызове процедуры POISK первым параметром является указатель на элемент массива структур NETRESOURCE. Мы копируем элемент массива в локальную переменную NR1. В принципе, в данной программе можно этого не делать, а сразу воспользоваться полученным указателем. В более сложных программах, однако, скорее всего, придется этот делать.

  4. Обратите внимание, что в процедуре вывода информации мы копируем строку в буфер, который потом используем для вывода. Это не прихоть, а необходимость. Дело в том, что перед выводом мы добавляем в конец строки коды 13 и 10. Поскольку мы выводим строки, которые потом используем для дальнейшего поиска, нам приходится использовать для вывода дополнительный буфер.

    Мы коснулись сетевого программирования весьма поверхностно. Заинтересованному читателю можем перечислить те вопросы, которые он мог бы изучить, воспользовавшись соответствующей литературой.

    Со следующего шага мы начнем рассматривать вопросы системного программирования в Windows.




Предыдущий шаг Содержание Следующий шаг