иконки интерактивных служб под Windows 7
От: kovbas  
Дата: 15.03.11 14:49
Оценка:
Добрый день
Существует стартующая с системной учетной записью интерактивная служба, успешно работающая под Win 2k, 2k3, XP
Под Windows 7 Pro 32x в трее (области уведомлений) не появляется иконка.
Отметка "Разрешить взаимодействие с рабочим столом" стоит.
Shell_NotifyIcon(NIM_ADD, ...)=FALSE даже если рабочий стол уже существует
GetLastError()=0
Вариант сделать клиента запускающегося не как служба не подходит
Re: иконки интерактивных служб под Windows 7
От: quodum  
Дата: 15.03.11 15:24
Оценка:
Здравствуйте, kovbas, Вы писали:

K>Вариант сделать клиента запускающегося не как служба не подходит


Ну пипец тогда.

Начиная с Висты, службы живут в отдельной сессии. Как, по-хорошему, и должно было быть с самого начала.

Ещё тут читать.
Re: иконки интерактивных служб под Windows 7
От: Mr.Delphist  
Дата: 15.03.11 15:30
Оценка: 1 (1)
Здравствуйте, kovbas, Вы писали:

K>Существует стартующая с системной учетной записью интерактивная служба, успешно работающая под Win 2k, 2k3, XP

K>Под Windows 7 Pro 32x в трее (области уведомлений) не появляется иконка.
Подозреваю, под Вистой у Вас такая же беда? Ибо начиная с Висты (если память не изменяет), MS назвала все интерактивные службы небезопасными.

K>Вариант сделать клиента запускающегося не как служба не подходит

Почему? Будет два (или более*) модуля, каждый из которых будет заниматься своим делом.

* Вы тестировали такой кейс, когда на серверной ОС одновременно залогинены два юзера (скажем, через remote desktop)?
Re: иконки интерактивных служб под Windows 7
От: okman Беларусь https://searchinform.ru/
Дата: 15.03.11 17:05
Оценка:
Здравствуйте, kovbas.

Воспользуйтесь поиском по форуму.
Сразу ссылок накидаю:

http://rsdn.ru/forum/winapi/4149500.aspx
Автор:
Дата: 09.02.11

http://rsdn.ru/forum/dotnet/3970406.aspx
Автор: Dan123
Дата: 24.09.10

http://rsdn.ru/forum/winapi/4005987.aspx
Автор:
Дата: 20.10.10

http://rsdn.ru/forum/winapi/3745539.aspx
Автор: Vis
Дата: 23.03.10
Re[2]: иконки интерактивных служб под Windows 7
От: kovbas  
Дата: 20.04.11 12:16
Оценка:
Здравствуйте, okman, Вы писали:

O>Здравствуйте, kovbas.


O>Воспользуйтесь поиском по форуму.

O>Сразу ссылок накидаю:

O>http://rsdn.ru/forum/winapi/4149500.aspx
Автор:
Дата: 09.02.11

O>http://rsdn.ru/forum/dotnet/3970406.aspx
Автор: Dan123
Дата: 24.09.10

O>http://rsdn.ru/forum/winapi/4005987.aspx
Автор:
Дата: 20.10.10

O>http://rsdn.ru/forum/winapi/3745539.aspx
Автор: Vis
Дата: 23.03.10


Родил следующий код:


bool SetUserDesktop ()
{
//    char buf [256];

    HWINSTA hWinstaOriginal = GetProcessWindowStation();
//    sprintf (buf, "hWinstaOriginal=0x%x", hWinstaOriginal);
//    flog.AddToBuf(buf, true);

    HWINSTA hWinstaInteractive = OpenWindowStation("WinSta0", 1, MAXIMUM_ALLOWED);
    bool b1 = SetProcessWindowStation(hWinstaInteractive);
//    sprintf (buf, "hWinstaOriginal=0x%x SetProcessWindowStation=%d", hWinstaInteractive, b1);
//    flog.AddToBuf(buf, false);

    HDESK hDesktop = OpenInputDesktop(0, 1/*??*/, MAXIMUM_ALLOWED);
//    sprintf (buf, "hDesktop=0x%x", hDesktop);

    BOOL b2 = SetProcessWindowStation(hWinstaInteractive);
//    sprintf (buf, "SetProcessWindowStation=%d", b2);

//    BOOL b4 = 0;
    if (!SetThreadDesktop(hDesktop))
    {
//        sprintf (buf, "SetThreadDesktop() GetLastError=%d", GetLastError());
//        flog.AddToBuf(buf, false);
//        flog.AppendAndCheckSize();
        return false;
    }
    flog.AddToBuf("Successful SetThreadDesktop()", false);
    return true;
    
//    BOOL b = SetProcessWindowStation(hWinstaInteractive);
};

void mainEntry()
{
  SetUserDesktop();
  DialogBox(g_hinst, MAKEINTRESOURCE(IDD_Metr), NULL, (DLGPROC)TestAppDlgProc);
  stopTask=1; 
  WaitForSingleObject(thread,INFINITE);
}

BOOL CALLBACK TestAppDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{
  HMENU menu;
  HWND c;
  POINT point;
  static int firstrun=1;
  switch (uMsg) 
  {
    case WM_INITDIALOG:
        {
            char buf[32];
//            flog.AddToBuf("WM_INITDIALOG\n");
            BOOL b1 = SetTimer(hDlg, gShowTimerId, 5000, 0);
            Dialog=hDlg;

            BOOL b2 = ShowWindow(Dialog, SW_SHOW);
            DWORD e = GetLastError();
            sprintf(buf, "WM_INITDIALOG: ShowWindow=%d\n", b2);
            flog.AddToBuf(buf);

            flog.AppendAndCheckSize();
//            NotifyAdd(hDlg);
        }
      break;
...
}


Трассировка показывет, что все в порядке. Запускаю службу под пользователем с системной учетной записью.
Как <Обнаружитель интерактивных служб> реагировал на ShowWindow(), так и реагирует.

ВОПРОС: Как правильно подменить рабочий стол системных служб на рабочий стол залогиненного пользователя в Windows 7 / Vista / 2008
Re[2]: иконки интерактивных служб под Windows 7
От: mike_rs Россия  
Дата: 21.04.11 07:37
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>* Вы тестировали такой кейс, когда на серверной ОС одновременно залогинены два юзера (скажем, через remote desktop)?

На самом деле проблемы с интерактивными службами начались еще с XP после появления fast user switching.
Re[3]: иконки интерактивных служб под Windows 7
От: okman Беларусь https://searchinform.ru/
Дата: 21.04.11 09:39
Оценка:
Здравствуйте, kovbas, Вы писали:

K>Родил следующий код:


...

K>Трассировка показывет, что все в порядке. Запускаю службу под пользователем с системной учетной записью.

K>Как <Обнаружитель интерактивных служб> реагировал на ShowWindow(), так и реагирует.

K>ВОПРОС: Как правильно подменить рабочий стол системных служб на рабочий стол залогиненного пользователя в Windows 7 / Vista / 2008


Сама постановка вопроса немного некорректна.
У служб вообще нету своего рабочего стола, а в Vista и выше они выполняются в
разных с пользовательскими процессами сессиях. И не забывайте, что сервис — почти как
консольный процесс, то есть никакие оконные сообщения до него не дойдут.
Могу посоветовать запустить из службы дочерний процесс от имени пользователя и весь
GUI-интерфейс организовать внутри этого процесса.

Но для того, чтобы писать код в этой области, нужно хотя бы в общих чертах
разбираться в таких понятиях, как пользовательская сессия (консольная или
терминальная), оконная станция, рабочий стол, контекст и маркер безопасности.
И вообще, понимание системы безопасности Windows тоже очень желательно, иначе
можно постоянно наступать на одни и те же грабли, не находя причин.
Очень рекомендую разобраться во всей этой "кухне".

Есть две разных схемы запуска дочерних интерактивных процессов из служб.

Первая из них довольно тривиальна (ее мы и рассмотрим подробно).
Если вкратце, то суть ее в том, чтобы получить маркер безопасности
пользователя и создать процесс с этим маркером. При этом не нужно проводить
никаких манипуляций с рабочими столами и оконными станциями, так как вся
нужная информация уже содержится внутри этого маркера.
Созданный процесс, за исключением очень незначительных деталей, ничем не
отличается от других, запущенных пользователем.
Единственная важная деталь — профиль пользователя должен быть загружен, иначе
созданному процессу будут недоступны кое-какие данные из Documents And
Settings и ветки реестра HKEY_CURRENT_USER.

Вторая схема более сложная и извращенная.
Ее в каком-то смысле можно назвать стыковкой — новый интерактивный процесс
создается в сессии пользователя, но при этом сохраняет все атрибуты безопасности
службы (LocalSystem), которая его запустила. Это более "веселенько" получается,
потому что порожденный процесс уже может взаимодействовать с рабочим столом
пользователя, отправлять и принимать оконные сообщения и так далее.
Для создания такого процесса требуется несколько не очень очевидных действий с
маркером безопасности (DuplicateToken, SetTokenInformation с ключом TokenSessionId и
некоторые другие). Разумеется, нет нужды говорить, что это является потенциальной
дырой в безопасности. Поэтому обычно рекомендуется не использовать для нового
процесса системный контекст, а создавать специальный ограниченный маркер
(см. CreateRestrictedToken). Если кого-то заинтересует, могу описать подробно.

Вот функция, которая запускает стандартный Windows-калькулятор на
рабочем столе определенного пользователя.

  Скрытый текст
/************************************************************************

Функция StartCalculator.
Открывает стандартный калькулятор (calc.exe) на рабочем столе
определенного пользователя. Предназначена только для запуска из службы (LocalSystem).
Возвращает TRUE в случае успеха и FALSE в случае ошибки.

*************************************************************************/

BOOL
_stdcall
StartCalculator()
{
    // Определяется Id активной пользовательской сессии.
    // Значение 0xFFFFFFFF означает что консольной сессии не существует.
    // Идентификатор сессии лучше всего получать в обработчике
    // событий службы, см. RegisterServiceCtrlHandler(Ex).
    // Там же можно определить консольная сессия или терминальная.
    // См. SERVICE_CONTROL_SESSIONCHANGE, WM_WTSSESSION_CHANGE,
    // WTSSESSION_NOTIFICATION, а также WTSQuerySessionInformation.

    DWORD IdSession = WTSGetActiveConsoleSessionId();

    if (IdSession == 0xFFFFFFFF)
    {
        return FALSE;
    }

    // Для того, чтобы действовать от имени пользователя, нужно
    // получить так называемый маркер безопасности, представляющий
    // этого пользователя в системе. Для этого существует функция
    // WTSQueryUserToken, она требует высоких привилегий, которые
    // обычно есть только у сервисов LocalSystem.

    HANDLE hUserToken;

    if (WTSQueryUserToken(IdSession, &hUserToken) == FALSE)
    {
        return FALSE;
    }

    // Перед созданием процесса в контексте пользователя необходимо
    // выполнить некоторые дополнительные действия.
    // Во-первых, нужно загрузить профиль пользователя - без этого
    // новому процессу будут недоступны данные, лежащие в папках
    // профиля и ключе реестра HKEY_CURRENT_USER.

    // Для загрузки профиля нужно знать имя пользователя.
    // Узнать его можно с помощью функции WTSQuerySessionInformation с
    // ключом WTSUserName, но дело в том, что при эта функция зависит
    // от службы терминалов, и если та еще не успела загрузиться (а такое
    // бывает нередко при включении компьютера и быстром входе в систему),
    // вызов WTSQuerySessionInformation вернет ошибку.
    // Это имеет значение, когда запуск интерактивного процесса должен
    // выполняться сразу же после входа пользователя.

    // Мы поступим по-другому - временно переключимся в контекст
    // безопасности пользователя и оттуда вызовем GetUserName.
    // Замечу, что без подмены маркера безопасности вызов GetUserName
    // будет возвращать имя SYSTEM (это распостраненная ошибка при попытке
    // получить имя пользователя из службы).

    // В MSDN сказано, что максимальная длина имени пользователя - 20 символов.
    // Добавим еще один для нулевого завершающего символа.
    
    wchar_t pUserName[21];
    DWORD cchUserNameBuffer = 21;    

    // Теперь данный поток временно будет действовать от имени пользователя.
    // Нужно учитывать, что при этом заимствуется также его контекст безопасности.
    // То есть, доступ к критическим системным объектам будет ограничен.
    
    if (ImpersonateLoggedOnUser(hUserToken) == FALSE)
    {
        CloseHandle(hUserToken);
        return FALSE;
    }

    if (GetUserNameW(pUserName, &cchUserNameBuffer) == FALSE)
    {
        RevertToSelf();
        CloseHandle(hUserToken);
        return FALSE;
    }

    // Возвращаем системный контекст "на место".
    
    RevertToSelf();

    // Заполняем нужные структуры и загружаем профиль пользователя.
    
    PROFILEINFOW prof;
    ZeroMemory(&prof, sizeof (prof));
    prof.dwSize     = sizeof (prof);
    prof.lpUserName = pUserName;
    prof.dwFlags    = PI_NOUI;

    if (LoadUserProfileW(hUserToken, &prof) == FALSE)
    {
        CloseHandle(hUserToken);
        return FALSE;
    }

    // Последнее приготовление - нужно подготовить переменные окружения.
    // Подробнее можно почитать в MSDN-справке к функции CreateProcessAsUser.

    void *pEnvBlock;

    if (CreateEnvironmentBlock(&pEnvBlock, hUserToken, FALSE) == FALSE)
    {
        UnloadUserProfile(hUserToken, prof.hProfile);
        CloseHandle(hUserToken);
        return FALSE;
    }

    // Создание процесса почти ничем не отличается от стандартного способа с
    // CreateProcess, только нужно указать маркер безопасности, от имени
    // которого этот процесс будет создан.

    STARTUPINFOW si;
    GetStartupInfoW(&si);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_SHOWNORMAL;
    si.lpDesktop = L"WinSta0\\Default";
    wchar_t pCmdLine[] = {L"calc.exe"};
    PROCESS_INFORMATION pi;

    if (CreateProcessAsUserW(hUserToken,
        NULL,
        pCmdLine,
        NULL,
        NULL,
        FALSE,
        NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT,
        pEnvBlock,
        NULL,
        &si,
        &pi) == FALSE)
    {
        DestroyEnvironmentBlock(pEnvBlock);
        UnloadUserProfile(hUserToken, prof.hProfile);
        CloseHandle(hUserToken);
        return FALSE;
    }

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);    
    DestroyEnvironmentBlock(pEnvBlock);

    // Эта функция должна вызываться при завершении созданного процесса.
    // UnloadUserProfile(hUserToken, prof.hProfile);

    CloseHandle(hUserToken);

    // Собственно, все !
    return TRUE;
}


Испытано практически на XP/Vista/Seven/Server2008, 32-64 бита.
запуск процесса из службы
Re: иконки интерактивных служб под Windows 7
От: okman Беларусь https://searchinform.ru/
Дата: 21.04.11 09:47
Оценка: 5 (1) +1
Здравствуйте, kovbas, Вы писали:

K>Добрый день

K>Существует стартующая с системной учетной записью интерактивная служба, успешно работающая под Win 2k, 2k3, XP
K>Под Windows 7 Pro 32x в трее (области уведомлений) не появляется иконка.
K>Отметка "Разрешить взаимодействие с рабочим столом" стоит.
K>Shell_NotifyIcon(NIM_ADD, ...)=FALSE даже если рабочий стол уже существует
K>GetLastError()=0
K>Вариант сделать клиента запускающегося не как служба не подходит

Кстати, Shell_NotifyIcon иногда возвращает ошибку, если пользователь уже вошел в систему, а
его интерактивное окружение (рабочий стол) еще не до конца загрузилось.
Надо просто организовать цикл и в нем вызывать Shell_NotifyIcon до тех пор, пока она
не вернет код успеха. Скажем, раз в секунду. Проверено.
Re[4]: иконки интерактивных служб под Windows 7
От: Jolly Roger  
Дата: 21.04.11 10:47
Оценка: 1 (1)
Здравствуйте, okman, Вы писали:

O>У служб вообще нету своего рабочего стола, а в Vista и выше они выполняются в

O>разных с пользовательскими процессами сессиях. И не забывайте, что сервис — почти как
O>консольный процесс, то есть никакие оконные сообщения до него не дойдут.

Что-то Вы тут в понятиях напутали Всякий процесс. в т.ч. сервисный, запускается в какой-то оконной станции(window station), и каждый поток связан с каким-то рабочим столом(Desktop). Оконные сообщения вполне могут доходить как до потоков консольки, так и сервиса. Другое дело, что есть ограничения на передачу сообщений между рабочими столами, и их нельзя передать в другую терминальную сессию.
"Нормальные герои всегда идут в обход!"
Re[4]: иконки интерактивных служб под Windows 7
От: kovbas  
Дата: 21.04.11 10:50
Оценка:
Здравствуйте, okman, Вы писали:

O>Сама постановка вопроса немного некорректна.

O>У служб вообще нету своего рабочего стола, а в Vista и выше они выполняются в
O>разных с пользовательскими процессами сессиях.

Если у служб нет своего рабочего стола, что тогда нам выводит сервис <Обнаружение интерактивных служб>?
На чем в этом случае располагается окно приложения?
Кстати, если это не рабочий стол, может его можно как-то минимизировать или сделать прозрачным?

O>Но для того, чтобы писать код в этой области, нужно хотя бы в общих чертах

O>разбираться в таких понятиях, как пользовательская сессия (консольная или
O>терминальная), оконная станция, рабочий стол, контекст и маркер безопасности.
O>И вообще, понимание системы безопасности Windows тоже очень желательно, иначе
O>можно постоянно наступать на одни и те же грабли, не находя причин.
O>Очень рекомендую разобраться во всей этой "кухне".

O>Есть две разных схемы запуска дочерних интерактивных процессов из служб.


O>Первая из них довольно тривиальна (ее мы и рассмотрим подробно).

O>Если вкратце, то суть ее в том, чтобы получить маркер безопасности
O>пользователя и создать процесс с этим маркером.
O>Единственная важная деталь — профиль пользователя должен быть загружен, иначе
O>созданному процессу будут недоступны кое-какие данные из Documents And
O>Settings и ветки реестра HKEY_CURRENT_USER.

Насколько я понял, это будет отдельный process, работающий с GUI и посылать message'и ему будет нельзя: фактически тоже самое что написать клиентскую программу работающую из Автозагрузки?

O>Вторая схема более сложная и извращенная.

O>Ее в каком-то смысле можно назвать стыковкой — новый интерактивный процесс
O>создается в сессии пользователя, но при этом сохраняет все атрибуты безопасности
O>службы (LocalSystem), которая его запустила. Это более "веселенько" получается,
O>потому что порожденный процесс уже может взаимодействовать с рабочим столом
O>пользователя, отправлять и принимать оконные сообщения и так далее.
O>Для создания такого процесса требуется несколько не очень очевидных действий с
O>маркером безопасности (DuplicateToken, SetTokenInformation с ключом TokenSessionId и
O>некоторые другие). Разумеется, нет нужды говорить, что это является потенциальной
O>дырой в безопасности. Поэтому обычно рекомендуется не использовать для нового
O>процесса системный контекст, а создавать специальный ограниченный маркер
O>(см. CreateRestrictedToken). Если кого-то заинтересует, могу описать подробно.

Что значит создается в сессии пользователя? В смысле служба не сможет стартовать с системной учетной записью?
И что именно создается: process или thread. Если process, то как он обменивается сообщениями?

O>Вот функция, которая запускает стандартный Windows-калькулятор на

O>рабочем столе определенного пользователя.

Насколько я понял, функция реализует 1 вариант?

O>Кстати, Shell_NotifyIcon иногда возвращает ошибку, если пользователь уже вошел в систему, а

O>его интерактивное окружение (рабочий стол) еще не до конца загрузилось.
O>Надо просто организовать цикл и в нем вызывать Shell_NotifyIcon до тех пор, пока она
O>не вернет код успеха. Скажем, раз в секунду. Проверено.

Я так и делаю. При запуске как службы Windows 7 эффекта нет никогда

Не подскажете также, почему если ставишь на Windows 7 программу в планировщик (при старте Windows) то интерфейс не появляется точно также как при старте службы?
Re[3]: иконки интерактивных служб под Windows 7
От: Jolly Roger  
Дата: 21.04.11 11:14
Оценка:
Здравствуйте, kovbas, Вы писали:

K>ВОПРОС: Как правильно подменить рабочий стол системных служб на рабочий стол залогиненного пользователя в Windows 7 / Vista / 2008


ОТВЕТ: Никак В этих системах сервисы запускаются в отдельной терминальной сессии, а для каждого вошедшего юзера создаётся своя терминальная сессия. Перепрыгнуть из одной такой сессии в другую возможности нет.
"Нормальные герои всегда идут в обход!"
Re[5]: иконки интерактивных служб под Windows 7
От: okman Беларусь https://searchinform.ru/
Дата: 21.04.11 14:15
Оценка: +1
Здравствуйте, kovbas, Вы писали:

K>Если у служб нет своего рабочего стола, что тогда нам выводит сервис <Обнаружение интерактивных служб>?


Выводится какой-то отдельный рабочий стол, который специально сделали для того, чтобы
перехватывать интерактивные (SERVICE_INTERACTIVE_PROCESS) службы.

K>На чем в этом случае располагается окно приложения?

K>Кстати, если это не рабочий стол, может его можно как-то минимизировать или сделать прозрачным?

Насколько я знаю — нельзя.

K>Насколько я понял, это будет отдельный process, работающий с GUI и посылать message'и ему будет нельзя: фактически тоже самое что написать клиентскую программу работающую из Автозагрузки?


Да. Отдельный процесс. Но совершенно такой же, как и все остальные, и сообщения до него тоже доходят.
Кстати, помимо сообщений есть всякие механизмы межпроцессных коммуникаций — пайпы, например...

K>Что значит создается в сессии пользователя? В смысле служба не сможет стартовать с системной учетной записью?

K>И что именно создается: process или thread. Если process, то как он обменивается сообщениями?

Служба создается и конфигурируется как обычно.
Потом ею создается дочерний процесс — он уже выполняется в сессии пользователя.
Обмен сообщениями выполняется через IPC, как говорилось выше. То есть, пайпы, файлы, события и тому подобное.

O>>Вот функция, которая запускает стандартный Windows-калькулятор на

O>>рабочем столе определенного пользователя.

K>Насколько я понял, функция реализует 1 вариант?


Да.

O>>Кстати, Shell_NotifyIcon иногда возвращает ошибку, если пользователь уже вошел в систему, а

O>>его интерактивное окружение (рабочий стол) еще не до конца загрузилось.
O>>Надо просто организовать цикл и в нем вызывать Shell_NotifyIcon до тех пор, пока она
O>>не вернет код успеха. Скажем, раз в секунду. Проверено.

K>Я так и делаю. При запуске как службы Windows 7 эффекта нет никогда


Видимо, что-то неправильно делаете.

K>Не подскажете также, почему если ставишь на Windows 7 программу в планировщик (при старте Windows) то интерфейс не появляется точно также как при старте службы?


Где-то я слышал, что планировщик Windows (в Vista и выше) запускает задания с более низкими, чем у
пользователя, правами. Может быть, из-за этого ? Я в том смысле, что программа, возможно, обращается к
каким-то объектам безопасности, а выполняясь под планировщиком, не имеет к ним доступа ?..
Re[5]: иконки интерактивных служб под Windows 7
От: okman Беларусь https://searchinform.ru/
Дата: 21.04.11 15:23
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Здравствуйте, okman, Вы писали:


O>>У служб вообще нету своего рабочего стола, а в Vista и выше они выполняются в

O>>разных с пользовательскими процессами сессиях. И не забывайте, что сервис — почти как
O>>консольный процесс, то есть никакие оконные сообщения до него не дойдут.

JR>Что-то Вы тут в понятиях напутали

Всякий процесс. в т.ч. сервисный, запускается в какой-то оконной станции(window station), и каждый поток связан с каким-то рабочим столом(Desktop). Оконные сообщения вполне могут доходить как до потоков консольки, так и сервиса. Другое дело, что есть ограничения на передачу сообщений между рабочими столами, и их нельзя передать в другую терминальную сессию.

Да, это я хватил лишку. Про оконные станции в курсе.
А вот на счет оконных сообщений непонятно — у сервисов же нет оконной процедуры.
Или Вы имеете в виду обработчик, который устанавливается с помощью RegisterServiceCtrlHandler ?
Re[6]: иконки интерактивных служб под Windows 7
От: Jolly Roger  
Дата: 21.04.11 15:47
Оценка:
Здравствуйте, okman, Вы писали:

O>А вот на счет оконных сообщений непонятно — у сервисов же нет оконной процедуры.


Процедур нет по как-бы умолчанию Но ведь нет никаких препятствий запустить цикл выборки-диспетчирезации, и отправлять сообщения с помощью PostThreadMessage. Или создать окно и диспетчировать из петли в него. Опять-же оконный таймер вполне можно использовать в сервисе, конечно при наличии в установившем его потоке петли сообщений. Ну и COM — COM вполне штатно работает в службе, но ему для маршалинга вызовов в STA необходимо наличие петли сообщений в потоке этого STA.

Надо только помнить, что сообщения окну можно передать только в пределах desktop'а, т.е. оба потока — получатель и отправитель — должны быть подключены к одному desktop'у. В случае использования PostThreadMessage этого не нужно, сообщения можно передавать между любыми потоками, но в пределах терминальной сессии.
"Нормальные герои всегда идут в обход!"
Re[7]: иконки интерактивных служб под Windows 7
От: okman Беларусь https://searchinform.ru/
Дата: 21.04.11 16:01
Оценка: +1 :)
Здравствуйте, Jolly Roger, Вы писали:

JR>Здравствуйте, okman, Вы писали:


O>>А вот на счет оконных сообщений непонятно — у сервисов же нет оконной процедуры.


JR>Процедур нет по как-бы умолчанию Но ведь нет никаких препятствий запустить цикл выборки-диспетчирезации, и отправлять сообщения с помощью PostThreadMessage. Или создать окно и диспетчировать из петли в него. Опять-же оконный таймер вполне можно использовать в сервисе, конечно при наличии в установившем его потоке петли сообщений. Ну и COM — COM вполне штатно работает в службе, но ему для маршалинга вызовов в STA необходимо наличие петли сообщений в потоке этого STA.


Интересно, что сделает система с таким сервисом после обработки им сообщения WM_QUERYENDSESSION ?

Надо будет как-нибудь поставить эксперимент...
Re[4]: иконки интерактивных служб под Windows 7
От: Maclaud  
Дата: 29.06.11 03:27
Оценка:
Здравствуйте, okman, Вы писали:

O>Здравствуйте, kovbas, Вы писали:


K>>Родил следующий код:


O>...


K>>Трассировка показывет, что все в порядке. Запускаю службу под пользователем с системной учетной записью.

K>>Как <Обнаружитель интерактивных служб> реагировал на ShowWindow(), так и реагирует.

K>>ВОПРОС: Как правильно подменить рабочий стол системных служб на рабочий стол залогиненного пользователя в Windows 7 / Vista / 2008


O>Сама постановка вопроса немного некорректна.

O>У служб вообще нету своего рабочего стола, а в Vista и выше они выполняются в
O>разных с пользовательскими процессами сессиях. И не забывайте, что сервис — почти как
O>консольный процесс, то есть никакие оконные сообщения до него не дойдут.
O>Могу посоветовать запустить из службы дочерний процесс от имени пользователя и весь
O>GUI-интерфейс организовать внутри этого процесса.

O>Но для того, чтобы писать код в этой области, нужно хотя бы в общих чертах

O>разбираться в таких понятиях, как пользовательская сессия (консольная или
O>терминальная), оконная станция, рабочий стол, контекст и маркер безопасности.
O>И вообще, понимание системы безопасности Windows тоже очень желательно, иначе
O>можно постоянно наступать на одни и те же грабли, не находя причин.
O>Очень рекомендую разобраться во всей этой "кухне".

O>Есть две разных схемы запуска дочерних интерактивных процессов из служб.


O>Первая из них довольно тривиальна (ее мы и рассмотрим подробно).

O>Если вкратце, то суть ее в том, чтобы получить маркер безопасности
O>пользователя и создать процесс с этим маркером. При этом не нужно проводить
O>никаких манипуляций с рабочими столами и оконными станциями, так как вся
O>нужная информация уже содержится внутри этого маркера.
O>Созданный процесс, за исключением очень незначительных деталей, ничем не
O>отличается от других, запущенных пользователем.
O>Единственная важная деталь — профиль пользователя должен быть загружен, иначе
O>созданному процессу будут недоступны кое-какие данные из Documents And
O>Settings и ветки реестра HKEY_CURRENT_USER.

O>Вторая схема более сложная и извращенная.

O>Ее в каком-то смысле можно назвать стыковкой — новый интерактивный процесс
O>создается в сессии пользователя, но при этом сохраняет все атрибуты безопасности
O>службы (LocalSystem), которая его запустила. Это более "веселенько" получается,
O>потому что порожденный процесс уже может взаимодействовать с рабочим столом
O>пользователя, отправлять и принимать оконные сообщения и так далее.
O>Для создания такого процесса требуется несколько не очень очевидных действий с
O>маркером безопасности (DuplicateToken, SetTokenInformation с ключом TokenSessionId и
O>некоторые другие). Разумеется, нет нужды говорить, что это является потенциальной
O>дырой в безопасности. Поэтому обычно рекомендуется не использовать для нового
O>процесса системный контекст, а создавать специальный ограниченный маркер
O>(см. CreateRestrictedToken). Если кого-то заинтересует, могу описать подробно.

O>Вот функция, которая запускает стандартный Windows-калькулятор на

O>рабочем столе определенного пользователя.

O>
  Скрытый текст
O>/************************************************************************

O>Функция StartCalculator.
O>Открывает стандартный калькулятор (calc.exe) на рабочем столе
O>определенного пользователя. Предназначена только для запуска из службы (LocalSystem).
O>Возвращает TRUE в случае успеха и FALSE в случае ошибки.

O>*************************************************************************/

O>BOOL
O>_stdcall
O>StartCalculator()
O>{
O>    // Определяется Id активной пользовательской сессии.
O>    // Значение 0xFFFFFFFF означает что консольной сессии не существует.
O>    // Идентификатор сессии лучше всего получать в обработчике
O>    // событий службы, см. RegisterServiceCtrlHandler(Ex).
O>    // Там же можно определить консольная сессия или терминальная.
O>    // См. SERVICE_CONTROL_SESSIONCHANGE, WM_WTSSESSION_CHANGE,
O>    // WTSSESSION_NOTIFICATION, а также WTSQuerySessionInformation.

O>    DWORD IdSession = WTSGetActiveConsoleSessionId();

O>    if (IdSession == 0xFFFFFFFF)
O>    {
O>        return FALSE;
O>    }

O>    // Для того, чтобы действовать от имени пользователя, нужно
O>    // получить так называемый маркер безопасности, представляющий
O>    // этого пользователя в системе. Для этого существует функция
O>    // WTSQueryUserToken, она требует высоких привилегий, которые
O>    // обычно есть только у сервисов LocalSystem.

O>    HANDLE hUserToken;

O>    if (WTSQueryUserToken(IdSession, &hUserToken) == FALSE)
O>    {
O>        return FALSE;
O>    }

O>    // Перед созданием процесса в контексте пользователя необходимо
O>    // выполнить некоторые дополнительные действия.
O>    // Во-первых, нужно загрузить профиль пользователя - без этого
O>    // новому процессу будут недоступны данные, лежащие в папках
O>    // профиля и ключе реестра HKEY_CURRENT_USER.

O>    // Для загрузки профиля нужно знать имя пользователя.
O>    // Узнать его можно с помощью функции WTSQuerySessionInformation с
O>    // ключом WTSUserName, но дело в том, что при эта функция зависит
O>    // от службы терминалов, и если та еще не успела загрузиться (а такое
O>    // бывает нередко при включении компьютера и быстром входе в систему),
O>    // вызов WTSQuerySessionInformation вернет ошибку.
O>    // Это имеет значение, когда запуск интерактивного процесса должен
O>    // выполняться сразу же после входа пользователя.

O>    // Мы поступим по-другому - временно переключимся в контекст
O>    // безопасности пользователя и оттуда вызовем GetUserName.
O>    // Замечу, что без подмены маркера безопасности вызов GetUserName
O>    // будет возвращать имя SYSTEM (это распостраненная ошибка при попытке
O>    // получить имя пользователя из службы).

O>    // В MSDN сказано, что максимальная длина имени пользователя - 20 символов.
O>    // Добавим еще один для нулевого завершающего символа.
    
O>    wchar_t pUserName[21];
O>    DWORD cchUserNameBuffer = 21;    

O>    // Теперь данный поток временно будет действовать от имени пользователя.
O>    // Нужно учитывать, что при этом заимствуется также его контекст безопасности.
O>    // То есть, доступ к критическим системным объектам будет ограничен.
    
O>    if (ImpersonateLoggedOnUser(hUserToken) == FALSE)
O>    {
O>        CloseHandle(hUserToken);
O>        return FALSE;
O>    }

O>    if (GetUserNameW(pUserName, &cchUserNameBuffer) == FALSE)
O>    {
O>        RevertToSelf();
O>        CloseHandle(hUserToken);
O>        return FALSE;
O>    }

O>    // Возвращаем системный контекст "на место".
    
O>    RevertToSelf();

O>    // Заполняем нужные структуры и загружаем профиль пользователя.
    
O>    PROFILEINFOW prof;
O>    ZeroMemory(&prof, sizeof (prof));
O>    prof.dwSize     = sizeof (prof);
O>    prof.lpUserName = pUserName;
O>    prof.dwFlags    = PI_NOUI;

O>    if (LoadUserProfileW(hUserToken, &prof) == FALSE)
O>    {
O>        CloseHandle(hUserToken);
O>        return FALSE;
O>    }

O>    // Последнее приготовление - нужно подготовить переменные окружения.
O>    // Подробнее можно почитать в MSDN-справке к функции CreateProcessAsUser.

O>    void *pEnvBlock;

O>    if (CreateEnvironmentBlock(&pEnvBlock, hUserToken, FALSE) == FALSE)
O>    {
O>        UnloadUserProfile(hUserToken, prof.hProfile);
O>        CloseHandle(hUserToken);
O>        return FALSE;
O>    }

O>    // Создание процесса почти ничем не отличается от стандартного способа с
O>    // CreateProcess, только нужно указать маркер безопасности, от имени
O>    // которого этот процесс будет создан.

O>    STARTUPINFOW si;
O>    GetStartupInfoW(&si);
O>    si.dwFlags = STARTF_USESHOWWINDOW;
O>    si.wShowWindow = SW_SHOWNORMAL;
O>    si.lpDesktop = L"WinSta0\\Default";
O>    wchar_t pCmdLine[] = {L"calc.exe"};
O>    PROCESS_INFORMATION pi;

O>    if (CreateProcessAsUserW(hUserToken,
O>        NULL,
O>        pCmdLine,
O>        NULL,
O>        NULL,
O>        FALSE,
O>        NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT,
O>        pEnvBlock,
O>        NULL,
O>        &si,
O>        &pi) == FALSE)
O>    {
O>        DestroyEnvironmentBlock(pEnvBlock);
O>        UnloadUserProfile(hUserToken, prof.hProfile);
O>        CloseHandle(hUserToken);
O>        return FALSE;
O>    }

O>    CloseHandle(pi.hProcess);
O>    CloseHandle(pi.hThread);    
O>    DestroyEnvironmentBlock(pEnvBlock);

O>    // Эта функция должна вызываться при завершении созданного процесса.
O>    // UnloadUserProfile(hUserToken, prof.hProfile);

O>    CloseHandle(hUserToken);

O>    // Собственно, все !
O>    return TRUE;
O>}
O>


O>Испытано практически на XP/Vista/Seven/Server2008, 32-64 бита.


Опиши пожалуйста второй способ.
У меня как раз задача, есть сервис и нужно чтобы он запускал себя же (в смысле свой exe) с теми же правами но в консольной сессии.
За ранее благодарю.
Re[5]: иконки интерактивных служб под Windows 7
От: okman Беларусь https://searchinform.ru/
Дата: 29.06.11 06:56
Оценка:
Здравствуйте, Maclaud, Вы писали:

M>Опиши пожалуйста второй способ.

M>У меня как раз задача, есть сервис и нужно чтобы он запускал себя же (в смысле свой exe) с теми же правами но в консольной сессии.
M>За ранее благодарю.

Несколько основных положений:

1. Сервис должен выполняться от имени LocalSystem, иначе некоторые функции будут недоступны.

2. При старте нужно подписаться на событие SERVICE_ACCEPT_SESSIONCHANGE.
Любые события, связанные со входом и выходом пользователя из системы, включая
терминальный доступ, будут проходить через обработчик (HandlerEx), а по параметрам
легко определить тип события (например, WTS_SESSION_LOGON, см. WTSSESSION_NOTIFICATION).
Я настоятельно рекомендую сделать демо-сервис с записью событий в журнал, а затем
поэкспериментировать с тем, какие события и когда вызываются, особенно в контексте
быстрого переключения пользователей и терминальных сеансов. Потому что кроме событий
logon/logoff есть еще connect/disconnect, которые вносят некоторую путаницу.

3. Id сессии, в которой произошло событие, нужно получать именно из обработчика, а
не путем всяких WTSGetActiveConsoleSessionId, потому что так можно будет вести учет того,
какие сеансы у нас запускаются, а какие отключаются.

4. Есть один подводный камень, связанный с запуском службы и перехватом первого логина.
Дело в том, что если используется простой вход в систему (минуя окно ввода имя
пользователя и пароля), служба в некоторых случаях может не успеть запуститься.
То есть, событие первого входа в систему не будет перехвачено.
Я не знаю, каков рекомендованный MSDN способ обхода этого ограничения и поступаю так —
всегда указываю принадлежность своей службы группе "Boot Bus Extender".
Радикально, но работает.

5. Id сессии, в которой произошел вход в систему, нужно запомнить.
Потому что это — "наше все". По нему можно определить имя пользователя, тип сеанса
(консольный или терминальный) и многие другие данные, см. WTSQuerySessionInformation.
Еще один ключевой показатель — маркер безопасности пользователя.
Получается с помощью WTSQueryUserToken.

Допустим, некто вошел в систему и мы знаем Id его сессии, а также получили маркер безопасности.
И нужно создать в его сессии интерактивный процесс, выполняющийся в контексте безопасности
нашей службы (LocalSystem).

Последовательность действий примерно такова:

1. Заполняем структуру STARTUPINFO, в lpDesktop пишем "WinSta0\Default".

2. Создаем новый маркер безопасности:

DuplicateTokenEx(hProcessToken, // получается с помощью OpenProcessToken.
    MAXIMUM_ALLOWED,
    NULL,
    SecurityDelegation,
    TokenPrimary,
    &hNewToken);


В созданном маркере устанавливаем Id сессии:

SetTokenInformation(hNewToken,
    TokenSessionId,
    &IdSession,
    sizeof (IdSession));


3. Создаем процесс с помощью CreateProcessAsUser, первым
аргументом передается новый маркер безопасности.
Созданный процесс будет отображаться в диспетчере задач как SYSTEM и его
нельзя будет "прибить" из-под неадминистраторской учетки, но при этом он
приаттачен к пользовательской сессии и полностью интерактивен.
Со всеми, так сказать, вытекающими.

Процессы, создаваемые описанным выше способом, имеют несколько особенностей.
Во-первых, происходит путаница с пользовательскими профилями.
Во-вторых, такой процесс представляет собой потенциальную дыру в безопасности.
В-третьих, при выходе из системы пользователя процесс уничтожается как и все
оконные — то есть, не позже обработки WM_ENDSESSION.

Есть и другие особенности, но они специфичны и к теме отношения не имеют.

Собственно, все.
Re[6]: иконки интерактивных служб под Windows 7
От: Maclaud  
Дата: 29.06.11 14:39
Оценка:
Здравствуйте, okman, Вы писали:

O>Здравствуйте, Maclaud, Вы писали:


M>>Опиши пожалуйста второй способ.

M>>У меня как раз задача, есть сервис и нужно чтобы он запускал себя же (в смысле свой exe) с теми же правами но в консольной сессии.
M>>За ранее благодарю.

O>Несколько основных положений:


O>1. Сервис должен выполняться от имени LocalSystem, иначе некоторые функции будут недоступны.


O>2. При старте нужно подписаться на событие SERVICE_ACCEPT_SESSIONCHANGE.

O>Любые события, связанные со входом и выходом пользователя из системы, включая
O>терминальный доступ, будут проходить через обработчик (HandlerEx), а по параметрам
O>легко определить тип события (например, WTS_SESSION_LOGON, см. WTSSESSION_NOTIFICATION).
O>Я настоятельно рекомендую сделать демо-сервис с записью событий в журнал, а затем
O>поэкспериментировать с тем, какие события и когда вызываются, особенно в контексте
O>быстрого переключения пользователей и терминальных сеансов. Потому что кроме событий
O>logon/logoff есть еще connect/disconnect, которые вносят некоторую путаницу.

O>3. Id сессии, в которой произошло событие, нужно получать именно из обработчика, а

O>не путем всяких WTSGetActiveConsoleSessionId, потому что так можно будет вести учет того,
O>какие сеансы у нас запускаются, а какие отключаются.

O>4. Есть один подводный камень, связанный с запуском службы и перехватом первого логина.

O>Дело в том, что если используется простой вход в систему (минуя окно ввода имя
O>пользователя и пароля), служба в некоторых случаях может не успеть запуститься.
O>То есть, событие первого входа в систему не будет перехвачено.
O>Я не знаю, каков рекомендованный MSDN способ обхода этого ограничения и поступаю так -
O>всегда указываю принадлежность своей службы группе "Boot Bus Extender".
O>Радикально, но работает.

O>5. Id сессии, в которой произошел вход в систему, нужно запомнить.

O>Потому что это — "наше все". По нему можно определить имя пользователя, тип сеанса
O>(консольный или терминальный) и многие другие данные, см. WTSQuerySessionInformation.
O>Еще один ключевой показатель — маркер безопасности пользователя.
O>Получается с помощью WTSQueryUserToken.

O>Допустим, некто вошел в систему и мы знаем Id его сессии, а также получили маркер безопасности.

O>И нужно создать в его сессии интерактивный процесс, выполняющийся в контексте безопасности
O>нашей службы (LocalSystem).

O>Последовательность действий примерно такова:


O>1. Заполняем структуру STARTUPINFO, в lpDesktop пишем "WinSta0\Default".


O>2. Создаем новый маркер безопасности:


O>
O>DuplicateTokenEx(hProcessToken, // получается с помощью OpenProcessToken.
O>    MAXIMUM_ALLOWED,
O>    NULL,
O>    SecurityDelegation,
O>    TokenPrimary,
O>    &hNewToken);
O>


O>В созданном маркере устанавливаем Id сессии:


O>
O>SetTokenInformation(hNewToken,
O>    TokenSessionId,
O>    &IdSession,
O>    sizeof (IdSession));
O>


O>3. Создаем процесс с помощью CreateProcessAsUser, первым

O>аргументом передается новый маркер безопасности.
O>Созданный процесс будет отображаться в диспетчере задач как SYSTEM и его
O>нельзя будет "прибить" из-под неадминистраторской учетки, но при этом он
O>приаттачен к пользовательской сессии и полностью интерактивен.
O>Со всеми, так сказать, вытекающими.

O>Процессы, создаваемые описанным выше способом, имеют несколько особенностей.

O>Во-первых, происходит путаница с пользовательскими профилями.
O>Во-вторых, такой процесс представляет собой потенциальную дыру в безопасности.
O>В-третьих, при выходе из системы пользователя процесс уничтожается как и все
O>оконные — то есть, не позже обработки WM_ENDSESSION.

O>Есть и другие особенности, но они специфичны и к теме отношения не имеют.


O>Собственно, все.


Спасибо за ответ. Я так понял тут основной момент это установка ID сессии в новом маркере.
А OpenProcessToken как процесс сервиса я так понял делается?
Re[7]: иконки интерактивных служб под Windows 7
От: Maclaud  
Дата: 29.06.11 15:33
Оценка:
CreateProcessAsUserA можно для этого использовать?
Re[7]: иконки интерактивных служб под Windows 7
От: okman Беларусь https://searchinform.ru/
Дата: 29.06.11 16:24
Оценка:
Здравствуйте, Maclaud, Вы писали:

M>Я так понял тут основной момент это установка ID сессии в новом маркере.


В целом — да.
Но есть еще нюанс, связанный с параметрами безопасности таких объектов,
как оконная станция и рабочий стол.
Вот в этом примере от MSDN явно просматривается, что теоретически может
потребоваться определенная настройка.

Я пробовал оба подхода,с изменением DACL и без, — результат одинаковый, хотя
вероятность того, что какой-нибудь сервис запустится раньше нашего и установит
более строгие параметры безопасности для оконной станции и рабочего стола,
списывать со счетов не стоит.

M>А OpenProcessToken как процесс сервиса я так понял делается?


Да.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.