Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Программирование и базы данных (http://forum.oszone.net/forumdisplay.php?f=21)
-   -   C/C++ *Example* | "Продвинутое" отключение XP (http://forum.oszone.net/showthread.php?t=29524)

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. Задавайте здесь, я отвечу.


hasherfrog 29-06-2004 09:36 204390

Есть маленькая "учечка памяти". Чтобы убрать:
Код:

...
    // если есть сессии, сообщаем об этом выключателю
    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 204391

hasherfrog
Хорошая программа.

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

hasherfrog 30-06-2004 09:02 204392

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

hasherfrog 30-06-2004 14:57 204393

Некоторые замечания сделаны здесь.

DAnG 01-07-2004 03:53 204394


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

Guest 15-08-2004 13:02 204395

Для написания таких программ лучше использовать Visual Basic.

Добавлено:

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

Добавлено:

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

[mzd] 15-08-2004 19:18 204396

Guest
Для системного программирования, IMHO, лучше С++ ничего нет.
VB для этого не разрабатывался


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

StasIs 12-09-2006 20:41 484047

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

hasherfrog 25-09-2006 11:35 489155

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


Время: 12:19.

Время: 12:19.
© OSzone.net 2001-