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


Автор: Danger 23.7.2009, 13:12
Привет всем,
Вобщем, есть терминальный сервер, удаленные пользователи через терминал запускают определенную программу (уникальный класс окна, создаваемый программой, знаю). Нужно "пробежаться" по всем сессиям, и проверить наличие в них окна с определенным классом.

Вот здесь уже обсуждалось: http://www.delphimaster.ru/cgi-bin/forum.pl?id=1239955786&n=5 попробовал, как описано - нифига не работает. Непонятно, как получить Desktop терминальной сессии ( WTSEnumerateSessions() перечисляет лишь имена сессий, но не windows stations). Максимум, могу получить список процессов терминальной сессии - но по ним же я не могу получить список окон, созданных этими процессами?

Кто знает, посоветуйте, пожалста..

Автор: kami 23.7.2009, 17:59
http://msdn.microsoft.com/en-us/library/aa383833(VS.85).aspx возвращает массив http://msdn.microsoft.com/en-us/library/aa383864(VS.85).aspx, один из параметров которой - pWinStationName.

Пройдя по массиву, найдя нужную терминальную сессию и WinSta, проходим по http://msdn.microsoft.com/en-us/library/ms682614(VS.85).aspx, находим нужный и http://msdn.microsoft.com/en-us/library/ms682615(VS.85).aspx.

Добавлено через 8 минут и 36 секунд
мда...
поторопился немного. Для того, чтобы перечислить десктопы нужно открыть winSta, а в http://msdn.microsoft.com/en-us/library/ms684339(VS.85).aspx не учитывается сессия...

Автор: kami 23.7.2009, 18:46
Цитата(Danger @  23.7.2009,  13:12 Найти цитируемый пост)
Максимум, могу получить список процессов терминальной сессии - но по ним же я не могу получить список окон

а если так:
WTSEnumerateProcesses>CreateToolhelp32Snapshot & Thread32First|Next>EnumThreadWindows .....

Автор: Danger 24.7.2009, 05:21
Цитата(kami @ 23.7.2009,  17:59)
Для того, чтобы перечислить десктопы нужно открыть winSta, а в http://msdn.microsoft.com/en-us/library/ms684339(VS.85).aspx не учитывается сессия...

Кстати, в процессе я так и не понял, что там с windows station в терминальных сессиях. То ли, используется текущая (консольная) window station ('WInSta0'), либо для всех терминальных сессий window station называется всегда одинаково (упомянутый 'WinSta0').

Вообще, существует ли способ перечислить не сессии, а window station терминальных сессий?

Насчет 
Цитата(kami @ 23.7.2009,  17:59)
WTSEnumerateProcesses>CreateToolhelp32Snapshot & Thread32First|Next>EnumThreadWindows .....

Неясно, как связать данные, получаемые через WTSEnumerateProcesses() и CreateToolhelp32Snapshot().
Точнее, непонятно зачем нужна CreateToolhelp32Snapshot(), если WTSEnumerateProcesses() и так вернет список терминальных процессов? Может, CreateToolhelp32Snapshot() не нужен, или я что-то не понимаю?

Автор: kami 24.7.2009, 12:33
Цитата(Danger @  24.7.2009,  05:21 Найти цитируемый пост)
 что там с windows station в терминальных сессиях

afaik, каждой терминальной сессии соответствует одна (СВОЯ) winSta. А вот название ее может отличаться:
Цитата

pWinStationName

Pointer to a null-terminated string that contains the WinStation name of this session. The WinStation name is a name that Windows associates with the session, for example, "services", "console", or "RDP-Tcp#0".



Цитата(Danger @  24.7.2009,  05:21 Найти цитируемый пост)
непонятно зачем нужна CreateToolhelp32Snapshot(), если WTSEnumerateProcesses() и так вернет список терминальных процессов? Может, CreateToolhelp32Snapshot() не нужен, или я что-то не понимаю?

Наоборот smile это я опять недосмотрел, что в CreateToolhelp32Snapshot при указании TH32CS_SNAPTHREAD будут перечисляться все потоки в системе, а не потоки текущего процесса.

Цитата(Danger @  24.7.2009,  05:21 Найти цитируемый пост)
WTSEnumerateProcesses() и так вернет список терминальных процессов?

ну да, но окна-то вроде можно перечислить только для потока, а не для процесса (EnumThreadWindows), соответственно - просто список процессов ничего не даст...

Автор: Danger 24.7.2009, 12:39
так что же делать?  smile я запутался, честно.

Автор: kami 24.7.2009, 12:52
попробуй так:
CreateToolhelp32Snapshot>Thread32First|Next>EnumThreadWindows>GetClassName.

Если нужно потом найти, в какой сессии это окно находится, то:
в Thread32First|Next>THREADENTRY32 есть параметр th32OwnerProcessID.
ProcessIDToSessionID, а зная SessionID можно получать о ней информацию из WTSQuerySessionInformation...

Автор: Danger 24.7.2009, 20:16
Цитата(kami @ 24.7.2009,  12:52)
попробуй так:
CreateToolhelp32Snapshot>Thread32First|Next>EnumThreadWindows>GetClassName.

Такое ощущение, что в слепок попадают только потоки, принадлежащие текущей window station.
Ни одного потока с процессов другого терминального пользователя..

Код

.....
//-----------------------------------------------------
function EnumThreadWndProc( Wnd: HWND; Param: LParam ): Boolean; stdcall;
var
 dwProcessId: DWord; strOwner: string;
 timeCreate: PFileTime;

begin
  Result:= true;  // we're enumerate all the thread windows

  ZeroMemory( @strWndClass, dwBufferSize );
  GetClassName( Wnd, strWndClass, dwBufferSize - 1 );

    dwProcessId:= PTHREADENTRY32( Param )^.th32OwnerProcessID;
    New( timeCreate );
    try
      if ( GetProcessParams( dwProcessId, strOwner, timeCreate ) ) then
      begin
        // using OEM codepage
        CharToOem( PChar( strOwner), PChar( strOwner ) );
        WriteLn( ' | ', dwProcessId, #9, '| ', FileTimeToStr( timeCreate ), #9, '| ', strOwner, #9, '|' );
      end;
     finally
       Dispose( timeCreate );
     end;
end;

//-----------------------------------------------------
begin
 .....

 // take a snapshot of all running threads
 hThreadSnap:= CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
 if ( hThreadSnap > 0 ) then
 begin

   te32.dwSize:= SizeOf( THREADENTRY32 );
   if ( Thread32First( hThreadSnap, te32 ) ) then
   begin
     EnumThreadWindows( te32.th32ThreadID, @EnumThreadWndProc, Integer( @te32 ) );
     // now walk the thread list of the system
     while ( Thread32Next( hThreadSnap, te32 ) ) do
      EnumThreadWindows( te32.th32ThreadID, @EnumThreadWndProc, Integer( @te32 ) );
   end;

   CloseHandle( hThreadSnap );
 end; // 'if ( hThreadSnap > 0 ) then '

//-----------------------------------------------------

GetProcessParams() - простейшая функция-"обёртка", возвращает некоторые параметры процесса..

Автор: kami 25.7.2009, 22:32
Так погляжу, на "соседнем" форуме посоветовали то, о чем я думал, но не хотел говорить - может есть менее обходной путь, чем запуск дополнительного процесса в каждой терминальной сессии...
Но мнение Eraser -а в таких вопросах можно считать непререкаемым.
Посему - уточняющие вопросы: версия ОС, в которой нужно найти нужное окно в терминальной сессии?

Автор: Danger 26.7.2009, 00:21
Цитата(kami @ 25.7.2009,  22:32)
Так погляжу, на "соседнем" форуме посоветовали то, о чем я думал, но не хотел говорить - может есть менее обходной путь, чем запуск дополнительного процесса в каждой терминальной сессии...

А что, если отталкиваться от списка процессов, возвращаемых WTSEnumerateProcesses() ?
Т.е. получить список процессов, потом для каждого из них потоки, и в них искать окно? (без создания снимков).

Цитата(kami @ 25.7.2009,  22:32)

Посему - уточняющие вопросы: версия ОС, в которой нужно найти нужное окно в терминальной сессии?

MS Windows 2003 VLK Russian (R2), 32bit.

Автор: kami 26.7.2009, 03:34
Цитата(Danger @  26.7.2009,  00:21 Найти цитируемый пост)
получить список процессов, потом для каждого из них потоки, и в них искать окно?

Не нашел функцию перечисления потоков по известному процессу. :( Покажете, как?

Цитата(Danger @  26.7.2009,  00:21 Найти цитируемый пост)
MS Windows 2003

Ага, значит предложенное Eraser-ом более чем возможно.

Автор: Danger 26.7.2009, 10:58
Цитата(kami @ 26.7.2009,  03:34)
Цитата(Danger @  26.7.2009,  00:21 Найти цитируемый пост)
получить список процессов, потом для каждого из них потоки, и в них искать окно?

Не нашел функцию перечисления потоков по известному процессу. :(
Я имею в виду, сделать снимок CreateToolhelp32Snapshot() по ProcessID терминального процесса, и перечислить потоки по Thread32First() / Thread32Next(). Хотя, боюсь, получится то же самое (но попробовать стоит).

Цитата(kami @ 26.7.2009,  03:34)
Цитата(Danger @  26.7.2009,  00:21 Найти цитируемый пост)

Цитата(Danger @  26.7.2009,  00:21 Найти цитируемый пост)
MS Windows 2003

Ага, значит предложенное Eraser-ом более чем возможно.
Проблема в том, что мне придется внедрять свой процесс в сессию каждого терминального пользователя, либо для каждого пользователя добавлять в автозагрузку прогу, постоянно висящую в памяти. И все это только для того, чтоб я когда-то мог узнать -  есть ли у пользователя на десктопе данное окно.

Автор: kami 26.7.2009, 13:01
Цитата(Danger @  26.7.2009,  10:58 Найти цитируемый пост)
делать снимок CreateToolhelp32Snapshot() по ProcessID терминального процесса, и перечислить потоки по Thread32First() / Thread32Next()

Цитата(kami @  24.7.2009,  12:33 Найти цитируемый пост)
 в CreateToolhelp32Snapshot при указании TH32CS_SNAPTHREAD будут перечисляться все потоки в системе, а не потоки текущего процесса.


Автор: Danger 26.7.2009, 15:06
Что характерно: запускаю Process Explorer от Sysinternals (в терминальной сессии), и он мне исправно перечисляет процессы других терминальных пользователей и потоки в них.
Да банально: диспетчер задач отображает все процессы, всех пользователей (с указанием имени пользователя и параметров процесса). Как-то же он это делает?

Автор: kami 26.7.2009, 20:45
Цитата(Danger @  26.7.2009,  15:06 Найти цитируемый пост)
запускаю Process Explorer от Sysinternals (в терминальной сессии), и он мне исправно перечисляет процессы других терминальных пользователей и потоки в них.

afaik, он использует свой драйвер, устанавливаемый на время его работы (драйвер находится в его ресурсах). Иначе многие из его возможностей (к примеру - закрытие любого хендла) из user-mode выглядят нереально.


Цитата(Danger @  26.7.2009,  15:06 Найти цитируемый пост)
диспетчер задач отображает все процессы, всех пользователей (с указанием имени пользователя и параметров процесса). Как-то же он это делает?

Да перечислить процессы и получить их параметры не так и сложно. А вот перечислить потоки процесса и окна потоков - с учетом 
Цитата(Danger @  24.7.2009,  20:16 Найти цитируемый пост)
Такое ощущение, что в слепок попадают только потоки, принадлежащие текущей window station.

не вижу решения, кроме предложенного eraser-ом.

P.S. А почему именно окно?
Нельзя будет обойтись запущен/не запущен процесс?

Автор: Danger 27.7.2009, 06:34
Цитата(kami @ 26.7.2009,  20:45)
Цитата(Danger @  26.7.2009,  15:06 Найти цитируемый пост)
запускаю Process Explorer от Sysinternals (в терминальной сессии), и он мне исправно перечисляет процессы других терминальных пользователей и потоки в них.

afaik, он использует свой драйвер, устанавливаемый на время его работы (драйвер находится в его ресурсах). Иначе многие из его возможностей (к примеру - закрытие любого хендла) из user-mode выглядят нереально.

Возможно. Кстати, я вышел даже на NtQuerySystemInformation() с недокументированным параметром для перечисления потоков, но результат ее использования аналогичен функциям из ToolHelp - в список исправно попадают все процессы/потоки, но только текущей сессии. Ни один процесс, принадлежащий другому пользователю, просто не попадает в список.

Цитата(kami @ 26.7.2009,  20:45)
Цитата(Danger @  26.7.2009,  15:06 Найти цитируемый пост)
диспетчер задач отображает все процессы, всех пользователей (с указанием имени пользователя и параметров процесса). Как-то же он это делает?

Да перечислить процессы и получить их параметры не так и сложно. А вот перечислить потоки процесса и окна потоков - с учетом 
Цитата(Danger @  24.7.2009,  20:16 Найти цитируемый пост)
Такое ощущение, что в слепок попадают только потоки, принадлежащие текущей window station.
P.S. А почему именно окно?
Нельзя будет обойтись запущен/не запущен процесс?

Довольно проблематично проверять "аутентичность процесса". По каким критериям лучше всего определять соответствие процесса нужному?

На первый взгляд, все параметры, по которым можно проверить, что запущен именно нужный исполнимый файл (работает нужный процесс) - ИМХО легко подделать обычному пользователю. Например, подменить exe-файл.

Автор: kami 27.7.2009, 17:39
Цитата(Danger @  27.7.2009,  06:34 Найти цитируемый пост)
все параметры, по которым можно проверить, что запущен именно нужный исполнимый файл (работает нужный процесс) - ИМХО легко подделать обычному пользователю. Например, подменить exe-файл.

Хм...
Обычный юзверь не должен иметь прав подменять рабочие экзешники (это если админ грамотный). Тем более он не должен иметь прав админа, чтобы дать себе такие права smile
Но и без этого проверить оригинальность файла можно: действительное полное имя файла, всякие crc32, MD5 и иже с ними, доп. информация о версии файла...

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