PDA

Показать полную графическую версию : C/C++ *Example* | "Продвинутое" отключение XP


hasherfrog
28-06-2004, 15:50
Несколько вопросов обсуждались в разное время на форуме.
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. Есть. Тут (http://hasherfrog.narod.ru/SmartDownXP.exe) пока лежит.

Q. Можно использовать в моих программах куски текста?
A. Да, сделано специально для посетителей www.oszone.net

Q. У меня ещё вопросы...
A. Задавайте здесь, я отвечу.

hasherfrog
29-06-2004, 09:36
Есть маленькая "учечка памяти". Чтобы убрать:
...
   // если есть сессии, сообщаем об этом выключателю
   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); // <--------- добавить.
        }
   }

   //чистим память от сообщения - оно нам больше не нужно
   free(pszSessions);
   ...

DAnG
29-06-2004, 21:13
hasherfrog
Хорошая программа.

Вопрос - как можно отслеживать все подключения (NetSessionEnum) и открытия файлов (NetFileEnum) для создания, скажем, своего журнала событий.
Если вызывать эти функции по таймеру - есть вероятность, что какое-то подключение или открытие файла успеет открыться и закрыться.
Возможно, нужно использовать механизм hook-ов, *можно ли сделать это без существенного снижения производительности системы?

hasherfrog
30-06-2004, 09:02
DAnG
Таймер однозначно "не катит". Хуки - это да :). Снижение производительности будет очень незначительным. У Руссиновича на www.sysinternals.com есть исходник, правда, для линукса. Там же есть бинарники FileMon. Если залезть туда (через HIEW, или ещё как) и посмотреть таблицу используемых системных функций, то хуки наверняка найдутся. Впрочем, я не проверял, так что это только предположение!

hasherfrog
30-06-2004, 14:57
Некоторые замечания сделаны здесь (http://forum.oszone.net/topic.cgi?forum=1&topic=4148&start=0#10).

DAnG
01-07-2004, 03:53
hasherfrog
Кучу времени пользуюсь и tcpview и regmon и filemon, а на сайт авторов так и не заглянул. Позор.
Очень много полезных вещей и с исходниками по windows, что редкость. Спасибо, огромное!.

Guest
15-08-2004, 13:02
Для написания таких программ лучше использовать Visual Basic.

Добавлено:

Кстати, кому надо, могу выложить исходник. Написал программку на VB, она умеет выключать/перезагружать комп, да ещё с такими наворотами (в смысле оформления)...

Добавлено:

И ещё, она может выключать/перезагружать комп в заданное время.

[mzd]
15-08-2004, 19:18
Guest
Для системного программирования, IMHO, лучше С++ ничего нет.
VB для этого не разрабатывался

Исправлено: [mzd], 19:20 15-08-2004

StasIs
12-09-2006, 20:41
Тема все еще акуальна а ссылки на скомпилиную прогу от hasherfrog уже мертвые :(
можно ли перезалить или на мыло stasisСОБАКАpisem.net сбросить? или кто перекомпилит поновой?

hasherfrog
25-09-2006, 11:35
Извиняюсь за задержку, в отпуске был.
http://hasherfrog.nm.ru/Trash/smrtdown.exe




© OSzone.net 2001-2012