Как я не однократно упоминал, любой программист должен знать, или хотя бы представлять себе как работает та или иная программа и уж тем более системная утилита. Это в первую очередь помогает понимать сам процесс взаимодействия операционной системы и программы. И следовательно создавать для себя, в качестве удобства администрирования свои собственные утилиты. Ну не пользоваться же в конце концов встроенными?
В этой статье и в последующих я рассмотрю разработку собственной утилиты для администрирования под управлением операционных систем Windows 2000/XP., которая позволит создавать пользователей, удалять их, редактировать профиль, изменять пароль и много другое.
А теперь приступим. Начнем с того, как научить нашу прогу получать список зарегистрированных пользователей. Для этого рассмотрим такую системную API-функцию как NetUserEnum. В MSDN’е она описана таким образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | NET_API_STATUS NetUserEnum( __in LPCWSTR servername, __in DWORD level, __in DWORD filter, __out LPBYTE *bufptr, __in DWORD prefmaxlen, __out LPDWORD entriesread, __out LPDWORD totalentries, __inout LPDWORD resume_handle ); |
Где первый параметр указывает на компьютер в сети. Если в качестве этого параметра указать nil, то система определит локальное имя компьютера.
Параметр level указывает системе о том какого уровня требуется информация об акаунте, более подробно их мы рассмотрим позже, но кому не терпится, могут почитать на MSDN’e. Но в примере мы рассмотрим уровень – 10. Он нам вернет имя акаунта, информацию об аккаунте.
Далее используется параметр в котором нужно указать по какому признаку фильтровать системную информацию. Как гласит справка MSDN их всего 6:
1 2 3 4 5 6 7 8 9 10 11 | FILTER_TEMP_DUPLICATE_ACCOUNT FILTER_NORMAL_ACCOUNT FILTER_PROXY_ACCOUNT FILTER_INTERDOMAIN_TRUST_ACCOUNT FILTER_WORKSTATION_TRUST_ACCOUNT FILTER_SERVER_TRUST_ACCOUNT |
Подробней о них в следующей статье, а в примере мы будем использовать FILTER_NORMAL_ACCOUNT.
В следующий параметр будет возвращен результат работы функции.
В следующем параметре указываем размер буфера.
В entriesread будет возвращено количество актуальных прочтенных записей, а в параметре totalentries – общее количество записей(зарегистрированных пользователей).
В последнем параметре будет возвращен указатель, для возможности продолжения работы.
В случае успешной отработки данной функции будет возвращено нулевое значение. В случае же ошибки будет возвращен один из следующих кодов ошибок:
1 2 3 4 5 | ERROR_ACCESS_DENIED NERR_InvalidComputer ERROR_MORE_DATA |
В связи с выбранным уровнем 10, будем исследовать следующую структуру, которая описана в справке MSDN:
1 2 3 4 5 6 7 8 9 10 11 | typedef struct _USER_INFO_10 { LPWSTR usri10_name; LPWSTR usri10_comment; LPWSTR usri10_usr_comment; LPWSTR usri10_full_name; } USER_INFO_10, *PUSER_INFO_10, *LPUSER_INFO_10; |
Так как наш язык программирования Delphi. Все выше описанное следует переделать под него, ну и не долго тянув кота за яйки, рассмотрим исходник получения списка пользователей в ОС Windows:
Delphi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs,Stdctrls, Comctrls; {$EXTERNALSYM NetUserEnum} function NetUserEnum(servername: LPWSTR; level, filter: DWORD; bufptr: Pointer; prefmaxlen: DWORD; entriesread, totalentries, resume_handle: LPDWORD): DWORD; stdcall; external 'NetApi32.dll' Name 'NetUserEnum'; function NetApiBufferFree(Buffer: Pointer): DWORD; stdcall; external 'NetApi32.dll' Name 'NetApiBufferFree'; type TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); const NERR_SUCCESS = 0; FILTER_TEMP_DUPLICATE_ACCOUNT = $0001; FILTER_NORMAL_ACCOUNT = $0002; FILTER_PROXY_ACCOUNT = $0004; FILTER_INTERDOMAIN_TRUST_ACCOUNT = $0008; FILTER_WORKSTATION_TRUST_ACCOUNT = $0010; FILTER_SERVER_TRUST_ACCOUNT = $0020; type TUSER_INFO_10 = record usri10_name, usri10_comment, usri10_usr_comment, usri10_full_name: PWideChar; end; PUSER_INFO_10 = ^TUSER_INFO_10; var dwERead, dwETotal, dwRes, res: DWORD; inf: PUSER_INFO_10; info: Pointer; p: PChar; i: Integer; begin info := nil; dwRes := 0; res := NetUserEnum(nil, 10, FILTER_NORMAL_ACCOUNT, @info, 65536, @dwERead, @dwETotal, @dwRes); if (res <> NERR_SUCCESS) or (info = nil) then Exit; p := PChar(info); for i := 0 to dwERead - 1 do begin inf := PUSER_INFO_10(p + i * SizeOf(TUSER_INFO_10)); memo1.Lines.Add(WideCharToString(PWideChar((inf^).usri10_name))); memo1.Lines.Add(WideCharToString(PWideChar((inf^).usri10_comment))); memo1.Lines.Add(WideCharToString(PWideChar((inf^).usri10_usr_comment))); memo1.Lines.Add(WideCharToString(PWideChar((inf^).usri10_full_name))); end; NetApiBufferFree(info); end; |
Когда вы взгляните на результат работы функции, у вас появиться вопрос, а зачем нам нужны технические пользователи созданные системой автоматически для своих нужд? Абсолютно с вами соглашусь, возвращенный результат содержит много побочной и не нужной инфы, однако, стоит знать все, что известно о пользователе, зарегистрированном в системе, все может пригодиться ;). А в следующей статье мы рассмотрим более подробно используемые структуры, другие системные функции для работы с пользователями и естественно возможность отсеивания не нужной инфы.