скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

SVR API для непродвинутых



Автор: Александр Терехов

Вступление...

Для тех, кто считает мои примеры и статьи "плагиатом без зазрения совести" ( были прецеденты от анонимных авторов), оговорюсь сразу: не я придумал систему Windows и API-функции к ней, а также не я писал хелп по этому поводу. Но это не значит, что использование полученной оттуда информации - есть плагиат, так как вся информация заранее застолблена авторским правом Б.Гейтса & сотоварищи. А раз она принадлежит им и только им, то какое-либо ее использование ни как не может подпадать под понятие "плагиат".

Иное дело алгоритмы...

Итак хелп.

Предыстория

Как-то потребовалось программным способом открыть доступ к сетевому ресурсу. После прочтения (в очередной раз) хелпа от Дельфи стало ясно, что сетевым администрированием занимаются функции семейства Net*. А в частности для открытия, закрытия доступа к сетевому ресурсу, а также получения или изменения информации о нем применяются функции подсемейства NetShare*. Однако, как написано в хелпе, все эти функции работают только под WinNT. К сожалению...

Но раз Win'9x открывает (закрывает) доступ, значит, можно сделать то же самое и программно. И тут начались поиски.

В хелпе сказано, что для Си++ надо применять заголовочный модуль LmShare.h, в котором даны прототипы функций библиотеки netapi32.dll. Посмотрев эту библиотеку утилитой tdump.exe (лежит в директории Delphi_X\Bin), я увидел, что библиотека netapi32.dll (для Win'9x) кроме одной экспортируемой функции netbios более ничего не содержит. Далее включаю обычный поиск текста "NetShareAdd" в файлах *.dll. Поиск выдал результат - среди нескольких библиотек эту строку содержит и библиотека svrapi.dll.

Следуя проторенным путем, т.е. через tdump.exe, увидел, что эта библиотека содержит почти все экспортируемые функции с названиями Net*. Ура! Место, где "собака порылась" найдено! Осталось только эту собаку откопать.

Ясно, что без знания прототипов этих функций, сами по себе их названия - пустой хлам. Включаю поиск в Интернет строки "svrapi". И вот он, заветный заголовочный модуль! SvrApi.pas. Лежит, голубчик на JEDI! Написал его Petr Vones, за что ему большое спасибо! В этом модуле даны прототипы следующих классов Net-функций:

  • ACCESS
  • SHARE
  • SESSION
  • CONNECTION
  • FILE
  • SERVER
  • SECURITY
Описание Net-функций приведено в хелпе от Дельфи. Их работа одинакова как для WinNT, так и для Win'9x. Правда, для Win'9x следует использовать несколько другие аргументы. О чем ниже и пойдет речь.

Функция NetShareAdd

Все семейство NetShare*, а это:

  • NetShareAdd - открывает доступ к сетевому ресурсу
  • NetShareDel - закрывает доступ к сетевому ресурсу
  • NetShareEnum - показывает количество и свойства ресурсов с открытым доступом
  • NetShareGetInfo - дает полную информацию об открытом сетевом ресурсе
  • NetShareSetInfo - меняет некоторые значения открытого сетевого ресурса

здесь я описывать не буду, т.к. намереваюсь выложить примеры для работы со всеми функциями NetShare. Опишу только одну - NetShareAdd

Итак функция NetShareAdd. Вот ее прототип:


function NetShareAdd(const pszServer: PChar; sLevel: SmallInt; pbBuffer:
  Pointer; cbBuffer: Word): NET_API_STATUS; stdcall;

Описание аргументов функции будут даны ниже. Сейчас рассмотрим, как следует подключать эту функцию.

Подключение внешней функции

Если не вдаваться в подробности, то в подключении внешних функций оказывается нет ничего сложного. В Unit.pas после описания класса TForm и до клаузы implementation следует написать:


function NetShareAdd(ServerName: PChar; Level: Integer; pbBuffer: Pointer;
  BufferSize: Integer): Integer; stdcall; external 'svrapi.dll';

На что следует обратить внимание. Во-первых, я вольно заменил типы некоторых переменных на более привычный Integer (хотя по большому счету этого делать не следует). Во-вторых, stdcall - это способ передачи данных через стек, применяемый для Паскаля. И, в-третьих, external 'svrapi.dll' - означает, что функция находится во внешней библиотеке с названием svrapi.dll.

Теперь к аргументам.

Аргументы функции

ServerName - сетевое имя компьютера, для локального можно писать Nill.

Что можно сказать об этом аргументе. Для работы с локальной машиной проблем не возникает. Например, если сетевое имя машины Toshiba, то в этот параметр надо писать '\\Toshiba' или указать Nill. А вот для сетевой машины возникают проблемы. Испытывая NetShareAdd в сети, я постоянно результатом работы функции получал значение 65, что означает - "Нет доступа к сети". Оказалось, чтобы открыть доступ к сети для NetShare-функций, надо "Разрешить удаленное управление этим сервером" (делается в "Пароли" из "Панели управления"). Тогда директории Windows присваивается сетевое имя ADMIN$ и после подключения этого имени в качестве сетевого диска, "сезам" открывается. Все это можно сделать программно: с помощью описываемой функции открыть доступ с именем ADMIN$ к любой директории, в том числе несуществующей (см. ниже) и с помощью одной из трех WNetAddConnection подключить сетевой диск для этого имени. Тогда все сработает на ура.

Level - уровень администрирования. Для WinNT применяют три уровня 1,2 и 502, для Win'9x следует применять уровень 50.

pbBuffer - указатель на структуру, в которую будем заносить все данные, необходимые для открытия доступа к ресурсу. На этой структуре следует остановиться более подробно. Вот она сама:


type
  TShareInfo50 = packet record
    shi50_netname: array[0..LM20_NNLEN] of Char; //сетевое имя
    shi50_type: Byte; //тип ресурса
    shi50_flags: Short; //флаг доступа
    shi50_remark: PChar; // комментарий
    shi50_path: PChar; // путь к ресурсу
    shi50_rw_password: array[0..SHPWLEN] of Char; //пароль полного доступа
    shi50_ro_password: array[0..SHPWLEN] of Char;
    //пароль "только чтение" доступа
  end;

shi50_netname - сетевое имя, по обращению к которому, будет доступен сетевой ресурс. Сетевое имя должно быть уникальным. Константа LM20_NNLEN имеет значение 12, т.е. сетевое имя не должно быть более 12-ти символов.

shi50_type - тип ресурса, может иметь следующие значения:
  • STYPE_DISKTREE = 0; - будем открывать доступ к директории;
  • STYPE_PRINTQ = 1; - будем открывать доступ к принтеру;
Есть еще две константы, но честно говоря, я их не тестировал:
  • STYPE_DEVICE = 2; - будем открывать доступ к коммуникационному устройству;
  • STYPE_IPC = 3; - открывает доступ к межпроцессорной коммуникации.

shi50_flags - флаг доступа, может иметь следующие значения:
  • SHI50F_RDONLY = $0001; - доступ только на чтение;
  • SHI50F_FULL = $0002; - полный доступ;
  • SHI50F_DEPENDSON = SHI50F_RDONLY or SHI50F_FULL; - доступ в зависимости от введенного пароля;
  • SHI50F_ACCESSMASK = SHI50F_RDONLY or SHI50F_FULL - доступ в зависимости от введенного пароля;
  • SHI50F_PERSIST = $0100; - ресурс будет записан в Registry и при перезагрузке компьютера доступ будет открываться в автомате. Для Win'95 почему-то доступ запоминается и без указания этого параметра.
  • SHI50F_SYSTEM = $0200; - в проводнике не будет видно, что доступ к этому ресурсу открыт.

shi50_remark - комментарий

shi50_path - полный физический путь к устройству. Здесь надо учесть следующее:
  1. Если путь указать строчными (маленькими) буквами, то в проводнике не будет видно, что доступ к устройству открыт.
  2. Если указать несуществующий путь, то доступ к такому устройству все равно будет открыт. Например, если открыть доступ к несуществующей папке C:\TEST333 и присвоить ей сетевое имя TEST66, то в сетевом окружении будет видно сетевое имя TEST66. Если затем создать папку с именем TEST333, то под этой папкой появится "синяя ручка".

shi50_rw_password - пароль для полного доступа;
shi50_ro_password - пароль "только чтение", где SHPWLEN = 8, т.е. максимальное количество символов в пароле с учетом символа под номером 0 составляет 9 штук.

И наконец, последний аргумент
BufferSize - размер буфера, в котором находятся необходимые для открытия доступа данные.

Теперь перейдем к значению, возвращаемому функцией NetShareAdd.

Результат функции

В случае успешного выполнения функции возвращается 0. В случае неудачи возвращается числовое значение. Обработка этого значения в большинстве случаев позволяет локализировать причину неудачи.

Для функции NetShareAdd предлагаются следующие виды ошибок (в моем вольном переводе):


NO_ERROR = 0; 'Все в порядке'
// из LmErr.pas:
NERR_BASE = 2100; базовая ошибка для NERR_;
NERR_NetNotStarted = NERR_BASE + 2 'Сеть недоступна';
NERR_UnknownServer = NERR_BASE + 3 'Неизвестный сервер';
NERR_ServerNotStarted = NERR_BASE + 14 'Сервер не работает';
NERR_UnknownDevDir = NERR_BASE + 16 'Сетевое устройство отсутствует';
NERR_RedirectedPath = NERR_BASE + 17 'Переназначенное устройство';
NERR_DuplicateShare = NERR_BASE + 18 'Сетевое имя уже существует';
NERR_BufTooSmall = NERR_BASE + 23 'Слишком маленький буфер для данных!';
NERR_NetNameNotFound = NERR_BASE + 210 'Сетевое имя не существует';
NERR_InvalidComputer = NERR_BASE + 251 'Неверное имя компьютера';
NERR_ShareNotFound = NERR_BASE + 292 'Сетевое устройство не обнаружено';

Однако бывают и другие ошибки. Почему-то функции SysErrorMessage(GetLastError) и WNetGetLastError при работе с NetShare* (по крайней мере у меня) не срабатывают, поэтому значения ошибок, коды которых менее 2100, я взял из ошибок, общих для всей операционной системы. (из Windows.pas:)


ERROR_NOT_ENOUGH_MEMORY = 8 'Недостаточно памяти';
ERROR_BAD_NETPATH = 53 'Неверное сетевое имя';
ERROR_NETNAME_DELETED = 64 'Сетевой ресурс более недоступен';
ERROR_NETWORK_ACCESS_DENIED = 65 'Отсутствует доступ к сети';
ERROR_BAD_DEV_TYPE = 66 'Неверный тип сетевого ресурса';
ERROR_BAD_NET_NAME = 67 'Не найдено сетевое имя';
ERROR_INVALID_PARAMETER = 87 'Неверный параметр';
ERROR_INVALID_LEVEL = 124 'Неверный уровень администрирования';

Т.е. если результатом функции возвращается число больше, чем 2100, то смотри модуль LmErr.pas, если меньше - то модуль Windows.pas.

На этом вроде все. Но, лучше один раз увидеть, чем... Поэтому посмотрим, как все работает на практике.

Пример работы NetShareAdd

Для директории C:\Temp локальной машины откроем доступ с именем "TEST", паролем для чтения "QWE", для полного доступа "ASDF", комментарием "This is a network machine's commentary", с автоматическим открытием доступа при перезагрузке компьютера. И заодно закроем доступ.

На новую форму следует положить две кнопочки и привести вид Unit1.pas в соответствие с ниже приведенным кодом.


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

//подключаем две функции из библиотеки svrapi.dll
function NetShareAdd(Servername: PChar; Level: Integer; Buffer: Pointer;
  BufferSize: Integer): Integer; stdcall; external 'svrapi.dll';
function NetShareDel(Servername: PChar; NetName: PChar; Reserved: DWORD): DWORD;
stdcall; external 'svrapi.dll';

//необходимые константы
const
  {см. LmCons.pas}
  LM20_NNLEN = 12;
  SHPWLEN = 8;
  SHI50F_RDONLY = 1;
  SHI50F_FULL = 2;
  STYPE_DISKTREE = 0;
  SHI50F_PERSIST = $0100;
  SHI50F_SYSTEM = $0200;
  {см. LmErr.pas}
  NERR_BASE = 2100;
  NERR_NetNotStarted = NERR_BASE + 2;
  NERR_UnknownServer = NERR_BASE + 3;
  NERR_ServerNotStarted = NERR_BASE + 14;
  NERR_UnknownDevDir = NERR_BASE + 16;
  NERR_RedirectedPath = NERR_BASE + 17;
  NERR_DuplicateShare = NERR_BASE + 18;
  NERR_BufTooSmall = NERR_BASE + 23;
  NERR_NetNameNotFound = NERR_BASE + 210;
  NERR_InvalidComputer = NERR_BASE + 251;
  NERR_ShareNotFound = NERR_BASE + 292;
  //формируем тип для записи с необходимыми параметрами
type
  TShareInfo50 = record
    shi50_netname: array[0..LM20_NNLEN] of Char; //сетевое имя
    shi50_type: Byte; //тип ресурса
    shi50_flags: Short; //флаг доступа
    shi50_remark: PChar; // комментарий
    shi50_path: PChar; // путь к ресурсу
    shi50_rw_password: array[0..SHPWLEN] of Char; //пароль полного доступа
    shi50_ro_password: array[0..SHPWLEN] of Char;
      //пароль "только чтение" доступа
  end; {Record}
var
  Form1: TForm1;
implementation
{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
  info50: TShareInfo50;
  rc, cb: Integer;
  ServerName, Path, NetName, ErrMes, ErrCap, Comment: string;
  MessIconBtn: Byte;
begin
  //установим необходимые параметры
  ServerName := '';
  Path := 'C:\TEMP';
  NetName := 'TEST';
  Comment := 'This is a network machine''s commentary';
  //заполним буфер
  FillChar(info50, sizeof(info50), 0);
  with info50 do
  begin {With}
    StrCopy(shi50_netname, PChar(NetName)); //сетевое имя
    shi50_type := STYPE_DISKTREE; //подключать будем диск
    shi50_remark := PChar(Comment); //комментарий
    shi50_flags := SHI50F_RDONLY or SHI50F_FULL //доступ определяется паролем
    or SHI50F_PERSIST; //и пишется в Registry
    shi50_path := PChar(Path); //путь
    StrPCopy(shi50_rw_password, 'ASDF'); //пароль для полного доступа
    StrPCopy(shi50_ro_password, 'QWE'); // пароль для "только чтение"
  end; {With}
  //установим размер буфера
  cb := sizeof(info50);
  //основная функция
  rc := NetShareAdd(PChar(ServerName), 50, @info50, cb);
  //сформируем текст сообщений об успехе или ошибках
  ErrMes := 'Доступ к устройству "' + NetName + '" открыт!';
  ErrCap := 'Все в порядке!';
  MessIconBtn := MB_OK or MB_ICONINFORMATION;
  //проверка ошибок
  if rc <> 0 then
  begin {ошибка}
    ErrCap := 'Ошибка!';
    MessIconBtn := MB_OK or MB_ICONERROR;
    case rc of
      //расшифровка ошибок
      ERROR_NOT_ENOUGH_MEMORY: ErrMes := 'Недостаточно памяти';
      ERROR_BAD_NETPATH: ErrMes := '"' + Servername +
        '" - неверное сетевое имя!';
      ERROR_NETNAME_DELETED: ErrMes := 'Сетевой ресурс более недоступен';
      ERROR_NETWORK_ACCESS_DENIED: ErrMes := 'Отсутствует доступ к сети';
      ERROR_BAD_DEV_TYPE: ErrMes := 'Неверный тип сетевого ресурса';
      ERROR_BAD_NET_NAME: ErrMes := 'Не найдено сетевое имя';
      ERROR_INVALID_PARAMETER: ErrMes := 'Неверный параметр';
      ERROR_INVALID_LEVEL: ErrMes := 'Неверный уровень администрирования';

      NERR_InvalidComputer: ErrMes := 'Неверное имя компьютера!';
      NERR_UnknownServer: ErrMes := 'Неизвестный сервер!';
      NERR_UnknownDevDir: ErrMes := 'Устройство "' + Path + '" отсутствует!';
      NERR_ServerNotStarted: ErrMes := 'Сервер не работает!';
      NERR_RedirectedPath: ErrMes := 'Переназначенный путь!';
      NERR_DuplicateShare: ErrMes := 'Сетевое имя "' + NetName +
        '" уже существует!';
      NERR_BufTooSmall: ErrMes := 'Слишком маленький буфер для данных!';
    else
    end; {Case}
  end; {ошибка}
  //выдадим сообщение
  MessageBox(Application.Handle, PChar(ErrMes), PChar(ErrCap), MessIconBtn);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  rc: DWord;
  Servername, NetName, ErrMes, ErrCap: string;
  MessIconBtn: Byte;
begin
  ServerName := '';
  NetName := 'TEST';
  rc := NetShareDel(PChar(ServerName), PChar(NetName), 0);
  ErrMes := 'Доступ к устройству "' + NetName + '" закрыт!';
  ErrCap := 'Все в порядке!';
  MessIconBtn := MB_OK or MB_ICONINFORMATION;

  if rc <> 0 then
  begin {ошибка}
    //ошибка
    ErrCap := 'Ошибка!';
    MessIconBtn := MB_OK or MB_ICONERROR;

    case rc of
      //расшифровка ошибок
      ERROR_BAD_NETPATH: ErrMes := '"' + Servername +
        '" - неверное сетевое имя!';
      ERROR_INVALID_PARAMETER: ErrMes := 'Неверный параметр!';
      NERR_NetNotStarted: ErrMes := 'Сеть недоступна!';
      NERR_ServerNotStarted: ErrMes := 'Сервер не работает!';
      NERR_NetNameNotFound: ErrMes := 'Устройство не существует!';
      NERR_ShareNotFound: ErrMes := 'Сетевое имя "' + NetName + '" не найдено!';
    else
      //смотри ошибки для NetShareAdd или
      ErrMes := 'Неизвестная сетевая ошибка!';
    end; {Case}
  end; {ошибка}
  MessageBox(Application.Handle, PChar(ErrMes), PChar(ErrCap), MessIconBtn);
end;

end.

Прилагаемые модули

Скачать SvrAPI.zip (28K)

  • SvrAPI.pas - модуль с прототипами Net-функций;
  • LmErr.pas - модуль с кодами ошибок;
  • LmCons.pas - модуль со значениями констант;
  • Examles - пример открытия доступа.

В приведенном примере можно было бы просто подключить эти модули в Uses и не мучиться с подключением функции, ее константами и кодами ошибок. Но мне кажется, что так интереснее и намного полезней. Да и каждый раз расшаркиваться перед правообладателем не надо! :)

Не забываем, что все, о чем говорилось в этой статье, относится к Win'9x!






Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook