Как получить список пользователей с сервера
Автор: Derek Lakin
Источник: www.sources.ru
Описание
Пример показывает, как пользоваться функцией
NetQueryDisplayInformation, для получения специфической информации о
пользователях, а так же представлен класс, который позволяет получить
более общий доступ к данному сервису.
Детальное описание функции
Прототип функции выглядит так: NET_API_STATUS NetQueryDisplayInformation(
LPCWSTR ServerName,
DWORD Level,
DWORD Index,
DWORD EntriesRequested,
DWORD PreferredMaximumLength,
LPDWORD ReturnedEntryCount,
PVOID *SortedBuffer);
Возвращаемый тип NET_API_STATUS определён как
DWORD . Если функция завершается успешно, то возвращаемое
значение будет ERROR_SUCCESS (соответствует Win32 коду 0).
Так же возможны следующие значения ошибки:
Значение |
Описание |
ERROR_ACCESS_DENIED |
Пользователь не имеет доступа к запрашиваемой
информации. |
ERROR_INVALID_LEVEL |
Параметр Level имеет неправильное
значение. |
ERROR_MORE_DATA |
Данная ошибка сигнализирует о том, что на сервере
доступно больше ячеек информации. Это говорит о том, что параметр
SortedBuffer содержит не последнюю доступную ячейку
информации. Для получения дополнительных данных, необходимо вызвать
NetQueryDisplayInformation заново с параметром Index ,
содержащем значение, возвращённое в next_index члене
последней ячейки в SortedBuffer. |
Так же возможно возникновение ошибки со значением 1722, которая
соответствует RPC_S_SERVER_UNAVAILABLE . Это говорит о том,
что сервер в данный момент не доступен. Конечто не обязательно, но
желательно включать в код программы проверку на возникновение данной
ошибки.
Первый параметр ServerName - это строка Unicode
wide-character, которая нам нужна, чтобы использовать
MultiByteToWideChar для преобразования стандартных строк в
необходимый формат. Он может быть NULL . При этом функция
вернёт информацию о локальном компьютере. Если же не NULL , то
строка должна начинаться с \\.
Особый интерес представляет параметр Level. Этот параметр указывает
уровень получаемой информации, и может иметь одно из следующих значений.
Значение |
Описание |
1 |
Получить информацию о пользователях. Параметр
SortedBuffer указывает на массив структуры
NET_DISPLAY_USER. |
2 |
Получить информацию о компьютере. Параметр
SortedBuffer указывает на массив структуры
NET_DISPLAY_MACHINE. |
3 |
Получить информацию о группе. Параметр
SortedBuffer указывает на массив структуры
NET_DISPLAY_GROUP. |
Так как нас интересует информация о пользователях, то устанавливаем
данный параметр в 1.
Теперь давайте посмотрим, как выглядит структура
NET_DISPLAY_USER : typedef struct _NET_DISPLAY_USER {
LPWSTR usri1_name;
LPWSTR usri1_comment;
DWORD usri1_flags;
LPWSTR usri1_full_name;
DWORD usri1_user_id;
DWORD usri1_next_index;
} NET_DISPLAY_USER, *PNET_DISPLAY_USER;
Не будем вдаваться в подробности описания каждого поля данной
структуры. Так как нас интересует только список пользователей, то обратим
внимание на два поля: usri1_name , в котором хранится логин
пользователя и usri1_full_name , в котором содержится полное
имя пользователя. Обе записи имеют формат Unicode wide-character строк.
Использование функции
Итак, при использовании данного сервиса рассмотрим три основных его
составляющих:
-
Установка параметров, для передачи в функцию.
-
Вызов функции и получение результатов.
-
Обработка возможных ошибок.
Параметры устанавливаются следующим образом:
CString szServer = "\\\\MYSERVER";
LPWSTR pWideServer;
int nBytesSource = strlen(pString) * 2;
int nWCharNeeded = MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED,
pString, nBytesSource, NULL, 0);
pWideServer = (LPWSTR)GlobalAlloc (GPTR, (nWCharNeeded + 1) * 2);
nWCharNeeded = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pString,
nBytesSource,(LPWSTR)pWideServer, nWCharNeeded);
if (0L == nWCharNeeded) {
pWideServer = NULL;
}
else {
*(LPWSTR)(pWideServer + nWCharNeeded) = L'\0';
}
nIndex = 0;
DWORD dwCount;
void* pBuffer;
NET_DISPLAY_USER* ndu;
DWORD dwResult, i;
Далее, для получения результатов используем следующий код: do {
dwResult = NetQueryDisplayInformation ((LPCWSTR)pWideServer,
1, nIndex, 10, 24, &dwCount, &pBuffer);
if ((dwResult == ERROR_SUCCESS) || (dwResult == ERROR_MORE_DATA)) {
for (i = 0, ndu = (NET_DISPLAY_USER*)pBuffer; i < dwCount; ++i, ++ndu) {
CString szName, szFullName, szComment;
szName.Format("%S", ndu->usri1_name);
szFullName.Format("%S", ndu->usri1_full_name);
szComment.Format ("%S", ndu->usri1_comment);
TRACE ("Name:\t\t" + szName + "\n");
TRACE ("Full Name:\t" + szFullName + "\n");
TRACE ("Comment:\t" + szComment + "\n");
TRACE ("--------------------------------\n");
if (dwCount > 0){
nIndex = ((NET_DISPLAY_USER *)pBuffer)[dwCount - 1].usri1_next_index;
}
}
}
} while (dwResult == ERROR_MORE_DATA);
Первый раз функция NQDI делает запрос для индекса 0. Далее индекс
увеличивается до тех пор, пока не будет получено очередных данных о
пользователе, либо не возникнет ошибка. Индекс следующего пользователя
содержится в поле usri1_next_index структуры
NET_DISPLAY_USER .
switch (dwResult) {
case ERROR_ACCESS_DENIED:
TRACE ("%s(%i): The user does not have access
to the requested information.\n", __FILE__, __LINE__);
break;
case ERROR_INVALID_LEVEL:
TRACE ("%s(%i): The Level parameter specifies
an invalid value.\n", __FILE__, __LINE__);
break;
case ERROR_MORE_DATA:
TRACE ("%s(%i): More entries are available.\n", __FILE__, __LINE__);
break;
case ERROR_SUCCESS:
break;
default: {
LPVOID lpMsgBuf;
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
0,
dwResult,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL );
TRACE ("%s(%i): %s\n", __FILE__, __LINE__, lpMsgBuf);
::LocalFree (lpMsgBuf);
}
break;
}
GlobalFree (pWideServer);
Класс для использования данного сервиса
Как и все хорошие программисты, я не смог не создать для этой функции
своего класса! Он имеет три основных статических функции и один
дополнительный класс CNetInfo : static DWORD GetUserInfo (USER_LIST* pUsers, LPCSTR pServer = NULL);
static DWORD GetMachineInfo (MACHINE_LIST* pMachines, LPCSTR pServer = NULL);
static DWORD GetGroupInfo (GROUP_LIST* pGroups, LPCSTR pServer = NULL);
static CString FormatMessage (DWORD dwError);
Первые три функции вызывают всё тот же NQDI. Последняя используется для
для приёма сообщений об ошибках и возвращает строку, содержащую описание
произошедшей ошибки.
Типы USER_LIST, MACHINE_LIST и GROUP_LIST определены как: #define USER_LIST CList<NET_DISPLAY_USER, NET_DISPLAY_USER&>
#define MACHINE_LIST CList<NET_DISPLAY_MACHINE, NET_DISPLAY_MACHINE&>
#define GROUP_LIST CList<NET_DISPLAY_GROUP, NET_DISPLAY_GROUP>
Так же определены уровни получаемой информации: #define LEVEL_USER 1
#define LEVEL_MACHINE 2
#define LEVEL_GROUP 3
Для использования данного класса, нам необходимо создать список
необходимых типов, передать указатель на него и имя сервера в требуемую
функцию (определённую параметром level), а затем, в зависимости от
результата, либо обработать полученные результаты, либо обработать
сообщение об ошибке. Класс определён в именном пространстве
ssl_net , так что необходимо добавить строчку using namespace
ssl_net; , либо вызывать функцию из класса, как это показано ниже:
USER_LIST pUsers = new USER_LIST;
CString szServer = "\\\\MYSERVER"
DWORD dwResult = ssl_net::CNetInfo::GetUserInfo (pUsers, szServer);
if (ERROR_SUCCESS == dwResult) {
POSITION pos = pUsers->GetHeadPosition ();
while (NULL != pos) {
NET_DISPLAY_USER ndu = pUsers->GetNext (pos);
CString szName, szComment, szFlags, szFullName, szUserID;
szName.Format ("%S", ndu.usri1_name);
szComment.Format ("%S", ndu.usri1_comment);
szFlags.Format ("%d", ndu.usri1_flags);
szFullName.Format ("%S",n du.usri1_full_name);
szUserID.Format ("%d", ndu.usri1_user_id);
TRACE ("%S\n%S\n%d\n%S\n%d\n", ndu.usri1_name,
ndu.usri1_comment, ndu.usri1_flags,
ndu.usri1_full_name, ndu.usri1_user_id);
}
}
else {
CString szErrMsg = ssl_net::CNetInfo::FormatMessage (dwResult);
AfxMessageBox (szErrMsg);
}
delete pUsers;
Теперь для того, чтобы это всё заработало, достаточно включить в проект
NetInfo.h и прилинковать NetApi32.lib.
Приведённый пример был написан на Visual C++ 6 SP4, но думаю, что код
должен работать с различными модификациями VC6 или сервиспаков.
Тестирование проводилось только на Windows NT 4 SP6a. В документации
сказано, что функция NQDI поддерживается в Windows NT 3.1 или поздних
версиях (включая Windows 2000), но не поддерживается в Windows 95 или
98.
|