Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > ScreenShot окна UAC в win7


Автор: yogin 9.4.2012, 22:01
Есть приложение, делает скрины каждые 10 секунд и сохраняет их на диске(регистратор визуальных действий). Недавно заметил, что окно "Контроль учётных записей пользователей" совсем не фоткается - его нет на скринах.
А оно так часто появляется... почти при запуске каждой проги.
Как его запечатлить? 
Скрин делается обычным способом:
Код

  DeskDC := GetDC(0);
  try
    BitBlt(..., SRCCOPY);
  finally
    ReleaseDC(0, DeskDC);      
  end;

Может нужно скринить как-то по другому?
Или ещё есть мысль - может быть нужно проматывать все существующие окна, смотреть на их какие-нибудь идентификаторы и определять UAC это окно или нет и если да, то скринить само окно через его дескриптор?
И следующий вопрос, а даст ли система свободу на все эти действия или тоже увильнёт как и от скрина?...

Автор: kami 9.4.2012, 22:27
UAC "выскакивает" на отдельном десктопе.
Порядок действия его, afair, следующий:
- скрин десктопа
- переключение на специфический десктоп
- отображение скрина
- создание окна "запустить/не запустить"

Соответственно, если скриншотящее приложение запущено под пользовательской терминальной сессией (и т.д.), то нужно пробовать SetThreadDektop, чтобы суметь сделать скрин этого специфического десктопа.

Afair, читал это в статьях Руссиновича.

Автор: kami 9.4.2012, 22:44
Цитата

The Winlogon desktop is active while a user logs on. The system switches to the default desktop when the shell indicates that it is ready to display something, or after thirty seconds, whichever comes first. During the user's session, the system switches to the Winlogon desktop when the user presses the CTRL+ALT+DEL key sequence, or when the User Account Control (UAC) dialog box is open.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682573(v=vs.85).aspx

Автор: yogin 10.4.2012, 18:21
Так... правильно ли я понял, 
что мне надо запомнить Handle текущего десктопа методом GetThreadDesktop(GetCurrentThreadId),
затем как-то узнать Handle десктопа, созданного UAC'ом, 
потом переключиться на него командой SwitchDesktop(), сделать скрин обычным методом через GetDC(0), а потом переключиться обратно тем же методом SwitchDesktop(OldDT) ?
Тогда вопрос, а как узнать хэндл окна UAC'а?

Или второй вариант: мне надо не GetDC(0) делать, а постоянно вызывать сначало GetThreadDesktop(GetCurrentThreadId), и уже относительно результата получать дескриптор?
Тогда вопрос, как получать дескриптор в этом случае? так: GetDC(GetThreadDesktop(GetCurrentThreadId)) ?

Автор: kami 11.4.2012, 13:13
не совсем так.
Есть ряд проблем с этой задачей.
1. Я не нашел функцию, позволяющую отследить смену десктопа, наподобие http://msdn.microsoft.com/en-us/library/windows/desktop/aa383841(v=vs.85).aspx, без этого имхо придется постоянно отслеживать через OpenInputDesktop
2. C учетом того, что UAC показывается на Winlogon -десктопе. http://msdn.microsoft.com/en-us/library/windows/desktop/ms682573(v=vs.85).aspx. Это печально. Вообще - читать http://msdn.microsoft.com/en-us/library/windows/desktop/ms686744(v=vs.85).aspx. Могу посоветовать обратиться к Eraser-у на форуме delphimaster.ru, он при создании своего RemoteOfficeManager как-то обошел это.
 
Если отбросить все сложности, то (имхо) должно выглядеть так:

1. Запоминаем текущий десктоп потока.
2. OpenInputDesktop
3. SetThreadDesktop
4. Снимок экрана
5. SetThreadDesktop(старый десктоп)
6. Освободить все полученные хендлы.

Кстати, не стоит забывать, что Winlogon - не единственный десктоп, отличный от winsta0\default. Есть куча программ (например, Марка Руссиновича), которые создают дополнительные десктопы и позволяют оперативно переключаться между ними. Иногда - очень полезная штука smile

Вечером еще пошукаю MSDN, но думаю нечего нового не скажу.

Автор: yogin 11.4.2012, 18:02
Цитата(kami @ 11.4.2012,  13:13)
Могу посоветовать обратиться к Eraser-у на форуме delphimaster.ru, он при создании своего RemoteOfficeManager как-то обошел это.

В ROM'е используется драйвер стандартного MS'овского удалённого рабочего стола. Так что это не то...

Автор: yogin 18.5.2012, 20:30
Правильно ли я понял, что перед каждым снимком мне надо создавать новый десктоп(OpenInputDesktop), потом фоткать обычным методом(GetDC(0)), а потом возвращаться к предыдущему десктопу. 
При этом фоткаться будет всё, т.к. мой новый десктоп будет как бы поверх всех существующих?...

Автор: kami 18.5.2012, 21:21
Цитата(yogin @  18.5.2012,  20:30 Найти цитируемый пост)
перед каждым снимком мне надо создавать новый десктоп(OpenInputDesktop), потом фоткать обычным методом(GetDC(0)), а потом возвращаться к предыдущему десктопу. 

Ну, OpenInputDesktop - это не создание нового десктопа, а попытка "подключения" к существующему.
Емнип(было дело - разрабатывал программу удаленного администрирования), нет. Достаточно один раз открыть и потом делать снимки. 
Опять-таки емнип, если делать снимки через BitBlt, то когда десктоп, DC которого получен, становится неактивным, то BitBlt начинает возвращать ошибку. Вот тогда и понадобится повторение всей последовательности от OpenInputDesktop и далее. Хотя... снимок раз в 10 секунд... можно каждый раз и полную последовательность, это не куча снимков за секунду.

Цитата(yogin @  18.5.2012,  20:30 Найти цитируемый пост)
При этом фоткаться будет всё, т.к. мой новый десктоп будет как бы поверх всех существующих?...

Вряд ли. хочу еще раз обратить внимание на:
Цитата

The Winlogon desktop's security descriptor allows access to a very restricted set of accounts, including the LocalSystem account. Applications generally do not carry any of these accounts' SIDs in their tokens and therefore cannot access the Winlogon desktop or switch to a different desktop while the Winlogon desktop is active.

Имхо - получить доступ к UAC-окну, даже если использовать запуск "из-под админа" у десктопного приложения не получится.

Автор: yogin 19.5.2012, 14:06
Кароч делаю так:
Код

var  
  bmp : TBitmap;
  DC  : HDC;
  olddesk, desk  : HDESK;
begin
  GetThreadDesktop(olddesk);
  desk := OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, False, MAXIMUM_ALLOWED);
  if desk > 0 then
  begin
    if SetThreadDesktop(desk) then
    begin
      DC := GetDC(0);
      if DC > 0 then
      begin
        bmp := TBitmap.Create;
        bmp.Height := Screen.Height;
        bmp.Width := Screen.Width;
        try
          bitblt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height, DC, 0, 0, SRCCOPY);
          bmp.SaveToFile(fAppPath + c_PTH08 + DateTimeToStr(Now) + '.bmp');
        finally
          ReleaseDC(0, DC);
          FreeAndNil(bmp);
        end;
      end;
      SetThreadDesktop(olddesk);
    end;
    CloseDesktop(desk);
  end;
end;

SetThreadDesktop всегда False, т.к.:
<<MSDN - function SetThreadDesktop - Remarks>>
Цитата

The SetThreadDesktop function will fail if the calling thread has any windows or hooks on its current desktop (unless the hDesktop parameter is a handle to the current desktop).

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

Автор: kami 19.5.2012, 17:42
Цитата(yogin @  19.5.2012,  14:06 Найти цитируемый пост)
Поэтому сдаётся мне процесс скрининга надо реализовывать сервисным приложением... 

к потоку в сервисе ведь точно такие же требования - отсутствие окон и хуков при переключении. Причем окно ведь не обязательно визуальное, хватит и таймера, в котором используется AllocateHwnd.

Цитата(yogin @  19.5.2012,  14:06 Найти цитируемый пост)
И видимо надо интерактивный сервис

В ОС, в которых появился UAC, интерактивные сервисы запрещены. В Vista была специализированная служба, которая отлавливала попытку отображения чего-то визуального интерактивным сервисом и предлагала показать "это" на специализированном десктопе, не имеющем никакого отношения к Winlogon-у. А вообще, интерактивные службы в Vista и 7 не являются такими, какими они задумывались изначально - они не имеют доступа даже к обычному десктопу обычного пользователя, так как находятся в отличной от пользовательской терминальной сессии. То есть - не являются интерактивными.
Цитата(yogin @  19.5.2012,  14:06 Найти цитируемый пост)
 чтобы скрин как-то передават "нормальному" приложению с формой, которое уже будет выполнять сохранение куда надо или отображение на форме...

Обычно делают наоборот smile

Автор: CodeMonkey 20.5.2012, 23:46
http://stackoverflow.com/questions/4046940/how-to-screen-shot-a-uac-prompt

Автор: yogin 22.5.2012, 12:07
Цитата(kami @  19.5.2012,  17:42 Найти цитируемый пост)
В ОС, в которых появился UAC, интерактивные сервисы запрещены.

Хорошо, тогда обычный сервис, но порядок действий-то будет в нём таким же?
Цитата(kami @  19.5.2012,  17:42 Найти цитируемый пост)
Обычно делают наоборот

Тогда просто самим сервисом сохранять файлы?

CodeMonkey, я сделал так как там написано(вручную отключил), но обычные методы скрининга всё равно не запечатлили окно UAC, лишь тень от него.

Автор: yogin 1.6.2012, 09:56
Опытным путём метода тыка выяснено:
1) что фотки UAC'а получаются в нормальном приложении, если это приложение запустить от имени системы...
2) а служба не может ничего сфоткать потому что ей в нормальном режиме десктопы не доступны(в 7-ке)...

Вытекающие вопросы:
1) как можно службой запустить приложение, чтобы у него были её права(или права системы), но при этом оно имело доступ к едсктопам?
2) можно ли как-то в винде назначить, чтобы прога автоматом запускалась с правами системы?

Автор: kami 1.6.2012, 13:13
Цитата(yogin @  1.6.2012,  09:56 Найти цитируемый пост)
а служба не может ничего сфоткать потому что ей в нормальном режиме десктопы не доступны(в 7-ке)...

Ага, у пользователя своя терминальная сессия, а у сервисов - своя. И у каждой своя WinSta и свой десктоп...

Цитата(yogin @  1.6.2012,  09:56 Найти цитируемый пост)
как можно службой запустить приложение, чтобы у него были её права(или права системы), но при этом оно имело доступ к едсктопам?

Единственное, что нашел - это http://msdn.microsoft.com/en-us/library/Aa379608. Но, хоть в community пишут, что она работает (только acl-ы нужно потом удалить, даже код есть как) у меня не получилось портировать это хозяйство на Delphi. Вернее, получилось (отрабатывает без ошибок), но процесс запускается не интерактивный, а непойми в какой WinSta...

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

На последний вопрос ничего не скажу - не знаю...

Добавлено @ 13:20
Кстати, не пробовал еще получать токен не через LogonUser, а через GetActiveConsoleSessionID и далее...

Автор: drkot 1.6.2012, 14:25
Цитата

1) как можно службой запустить приложение, чтобы у него были её права(или права системы), но при этом оно имело доступ к едсктопам?


Сразу оговорюсь... то что напишу не есть "правильно" с точки зрения системного подхода ОС, но значительно проще и гуманнее относительно программиста.
Принцип прост: любое приложение может посылать (создавать) записи в системном журнале событий. В системном планировщике есть возможность привязки задачи к журналу событий.
Для реализации такого механизма не нужно ковырять права и переключаться между сессиями... что значительно упростит задачу для начинающего.

Права на запуск задаются в том же планировщике

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)