hasherfrog |
28-06-2004 15:50 204389 |
C/C++ *Example* | "Продвинутое" отключение XP
Несколько вопросов обсуждались в разное время на форуме.
1. Как заставить ХР предупредить о подключенных соединениях при выключении машины;
2. Как прервать отключение машины, если что-то забыл сделать.
Поскольку я сейчас чем-то подобным занимаюсь, набросал небольшую программку, предназначенную для отключения XP машин. Её можно использовать вместо стандарного Shutdown в меню Пуск.
Код:
#ifndef UNICODE
#define UNICODE
#endif
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <windows.h>
#include <winuser.h>
#include <lm.h>
// собранная информация о сессиях
static wchar_t * pszSessions = NULL;
// добавление очередной записи к хранилищу
void save_session(const wchar_t * fmt, ...)
{
// размер очередной строки, ориентировочно ~100 байт
int size = 100;
// память под очередную строку
wchar_t *p = (wchar_t *) malloc(size * sizeof(wchar_t));
// безопасность
if (!p) return; p[0] = '\0';
// формируем строку
va_list ap;
while (1)
{
// попытаемся вместиться в уже выделенную память
va_start(ap, fmt);
// реальная длина новой строки
int n = _vsnwprintf(p, size, fmt, ap);
va_end(ap);
// если влезли, ок
if (n > -1 && n < size) break;
// иначе - увеличиваем буфер под строку
size *= 2;
if ((p = (wchar_t *) realloc(p, size * sizeof(wchar_t))) == NULL) return; //oops
}
// строку сформирована, надо её добавить к имеющимся
size = lstrlen(p);
if (pszSessions) size += lstrlen(pszSessions);
wchar_t *pn = (wchar_t *)malloc((size + 1)*sizeof(wchar_t)); //eos
if (!pn) return; pn[0] = '\0';
//складываем строки
if (pszSessions) lstrcpy(pn, pszSessions);
lstrcat(pn, p);
free(p);
free(pszSessions);
pszSessions = pn;
return;
}
//int main(int argc, wchar_t *argv[])
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrev,LPSTR lpszCmd,int nCmd)
{
DWORD dwTotalCount = 0;
NET_API_STATUS nStatus;
// начальная фраза сообщения
save_session(L"%s", L"Warning! Some sessions are not closed:\n\n");
// для всех имеющихся соединений
do
{
LPSESSION_INFO_10 pBuf = NULL;
DWORD dwEntriesRead = 0;
DWORD dwTotalEntries = 0;
DWORD dwResumeHandle = 0;
// вызываем очередной NetSessionEnum
nStatus = NetSessionEnum(NULL, //всё локально
NULL, //для всех сессий
NULL, //для всех пользователей
10, //тип запроса хост-юзер-время-простой
(LPBYTE*)&pBuf, //данные на выходе
MAX_PREFERRED_LENGTH, //длина записи
&dwEntriesRead, //сколько считано
&dwTotalEntries, //сколько всего
&dwResumeHandle); //номер запроса
// проверяем результат
if ((nStatus == NERR_Success) || (nStatus == ERROR_MORE_DATA))
{
// запоминаем сессии клиентов
if (LPSESSION_INFO_10 pTmpBuf = pBuf)
for (int i = 0; i < dwEntriesRead; i++)
if (pTmpBuf)
{
save_session(L"Client: %s\tUser: %s\tActive: %d\tIdle: %d\n",
pTmpBuf->sesi10_cname, pTmpBuf->sesi10_username,
pTmpBuf->sesi10_time, pTmpBuf->sesi10_idle_time);
pTmpBuf++;
dwTotalCount++;
}
}
// идём на следующий запрос
if (pBuf)
{
NetApiBufferFree(pBuf);
pBuf = NULL;
}
} while (nStatus == ERROR_MORE_DATA);
// если есть сессии, сообщаем об этом выключателю
if (dwTotalCount)
{
// финальная часть сообщения
save_session(L"%s", L"\nDo you wish to cancel shutdown?\n");
// вывод сообщения
if (IDYES == MessageBox(NULL, pszSessions, L"Warning!", MB_YESNO)) return 0;
}
//чистим память от сообщения - оно нам больше не нужно
free(pszSessions);
HANDLE hToken; // handle to process token
TOKEN_PRIVILEGES tkp; // pointer to token structure
// Get the current process token handle so we can get shutdown privilege.
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken)) return 1;
// Get the LUID for shutdown privilege.
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1; // one privilege to set
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// Get shutdown privilege for this process.
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
if (GetLastError() != ERROR_SUCCESS) return 1;
// Вывести окошечко обратного отсчёта
if (!InitiateSystemShutdown(
NULL, // shut down local computer
L"We starts system shutdown.\nIf You have forget to do something, press "
L"Ok in message box for stop shutdown.", // message for user
20, // time-out period
FALSE, // ask user to close apps
FALSE)) //shutdown, no reboot
return 1;
// Disable shutdown privilege.
tkp.Privileges[0].Attributes = 0;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
MessageBox(NULL, L"Press OK to stop shutdown", L"Haves forget somehing?",
MB_OK | MB_SYSTEMMODAL);
// если мы сюда попали, надо отключать шатдаун
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken)) return 1;
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
if (GetLastError() != ERROR_SUCCESS) return 1;
// Prevent the system from shutting down.
if ( !AbortSystemShutdown(NULL) ) return 1;
// Disable shutdown privilege.
tkp.Privileges[0].Attributes = 0;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
return 0;
}
FAQ
Q. И как использовать?
A. Компилите. Кладёте ярлык на рабочий стол. При необходимости выключить машину жмёте не Пуск->Выключить, а щёлкаете по этому ярлыку. Он Вас спросит про активные подключения (если они есть). Потом даст 20 секунд на раздумья. Потом выключится.
Q. Почему C++, а не, скажем, VBS?
A. Потому что C++ мне ближе.
Q. Как компилить?
A. Я использую Visual C++ Toolkit. Компиляция:
cl enum_shares.cpp /GA /O1 /link /defaultlib:netapi32.lib /defaultlib:advapi32.lib /defaultlib:user32.lib
Q. А в 9х будет работать?
A. Нет. В 9х сообщение об активных подключениях и так выдается.
Q. Почему UNICODE?
A.Потому что NetSessionEnum
Q. Что так странно (в смысле стиля написания) сформирован текст и комментарии?
A. Потому что при написании использовались фрагменты из PSDK.
Q. А есть ли бинарник?
A. Есть. Тут пока лежит.
Q. Можно использовать в моих программах куски текста?
A. Да, сделано специально для посетителей www.oszone.net
Q. У меня ещё вопросы...
A. Задавайте здесь, я отвечу.
|