Отправляет email-рассылки с помощью сервиса Sendsay

Программирование. Форум !!!

Найти иконку в трее

Hello comp,

Приходится писать проект по автоматизации закачки из Инета.
Подключение к Инету должно происходить через оптимизатор модема
(скорость при этом, действительно, увеличивается на 20-50%). Сам
оптимизатор сидит в трее, и соединение инициализируется щелчком по его
иконке. Самое интересное, что по другому заставить заработать этот
оптимизатор нет.
Вот я и думал организовать процесс вызова так:
Поставить указатель в трей с помощью SetCursorPos в конец трея
Задавая приращения по горизонтали, обойти указателем иконки в
трее. При этом будут появляться ярлычки подсказок к иконкам. Вот их-то
и нужно проверять. Наблюдая за изменениями в "оконной среде" системы через
WinSight, я увидел, что эти ярлычки "привязываются" к popup-окнам с
именем класса tooltips_class32. Как я понимаю, это значит, что если с
помощью FindWindow и GetWindow обойти все окна с таким именем класса, то
GetWindowText может определить текст ярлыка, и таким образом моя прога
определит, над нужной ли иконкой находится мышь. Ну а спровоцировать
мышь на щелчок - уже дело техники.
Да вот, что-то не получается у меня отыскать в списке окон окно с
ярлычком. Я думаю, может моя прога не успевает по времени "ухватиться"
за этот ярлык. Или я концептуально ошибаюсь?

Ответить   Вадим Шешунов Fri, 10 Feb 2006 09:56:27 +0200 (#513165)

 

Ответы:

А у меня панель задач вдвое выше и автоскрывается. Мало того, что её
показать надо, прыгнув мышью в самый низ экрана и подождав, так ещё и иконки

в трее в три ряда выстраиваются. Каким образом ты собрался все их обойти по
горизонтали? И это ещё при том, что XPя по умолчанию скрывает некоторые
"неактивные" достаточно долгое время иконки за эдакой стрелочкой, и чтобы их

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

окон" - как меня нервирует, кстати.
Ну и чтобы добить. Ждать над каждой иконкой, нужно неопределённое время,

так как частенько обработчик рабочего стола неизвестно чем занимается вместо

того, чтобы побыстрому обработать событие, ему направленное. У меня иногда
бывает, что рабочий стол и панель задач не откликаются на воздействия извне,

хотя остальная винда работает как ни в чём не бывало, и все мышиные и
клавиатурные пинки не возымевают никакого действия. По прошествии
полутора-двух минут, они друг приходят в себя и побыстрому обрабатывают всё
то, что им быто понапнуто за это время. Весело бывает понаблюдать...
Напрягает не меньше. Если кто знает лекарство или хотя бы причину такого
поведения, буду рад любой информации.
Так что ИМХО избранный тобою подход в корне неверен. Я в принципе
недолюбливаю всё то, что делается потём эмуляции действий пользователя, так
как программно всей изощрённости человеческого разума не проэмулируешь.
Особенно его экстраваннтности и индивидуальности. Посему начисто отвергаю
решения, основанные на mouse_event() и keybd_event() Да и в MSDN чётко
говорится, что они заменены (superseded). Ищи другие пути, не то запаришся
поддерживать свою программу - пользователи задолбают.
Вот начало. Окно панели задач относится к классу Shell_TrayWnd. Одно из
его дочерних окон имеет класс TrayNotifyWnd, а у него в свою очередь одно из

дочерних - класс SysPager, которое в свою очередь одним из дочерних окно
класса ToolbarWindow32 и с заголовком (в русской редакции XP!) "Область
уведомлений", в котором уже и прорисовываются иконки. Кнопка, которая
открывает скрытые "неактивные" иконки, имеет класс Button и является
дочерним окном непосредственно от панели задач. Хватит информации для
начала? Всю её я получил простым разглядыванием Spy++ из комплекта MS
DevStudio. Но её понятное дело можно получить и программно - EnumWindows(),
EnumChildWindows(), GetWindow(), GetWindowInfo(), GetClassName() итп Скорее
всего некоторые неопределённости останутся, но ИМХО значительно меньше и
более легко обходимые, чем я привёл вначале по твоему методу.

P.S. Чуть не забыл: mouse_event() и keybd_event() заменены на
SendInput()

--
С уважением, boroda

Номер выпуска : 5188
Возраст листа : 873 (дней)
Количество подписчиков : 546
Адрес в архиве : http://subscribe.ru/archive/comp.soft.prog.prog/msg/513477
Получить правила : mailto:comp.soft.prog.prog-rules@subscribe.ru
Формат "дайджест" : mailto:comp.soft.prog.prog-digest@subscribe.ru
Формат "каждое письмо" : mailto:comp.soft.prog.prog-normal@subscribe.ru
Формат "читать с веба" : mailto:comp.soft.prog.prog-webonly@subscribe.ru

Ответить   Шматко А.А. Fri, 10 Feb 2006 21:20:24 +0300 (#513477)

 

Здравствуйте, Шматко.

Friday, February 10, 2006, 8:20:24 PM, you wrote:

по

их

всё

так

Все это правильно, конечно. Только пишу это все для своего домашнего
компа - надоело вставать в час ночи, чтобы поставить на закачку. Так
что, "пользователи" не задалбают, особенно если я их пивом
угощу.-)))))))))

из

из

Я это тоже рассматривал, только чё-то не получил тех хэндлов, на
которые ты намекаешь. Порассматриваю, значит, более внимательно.

ОК! Спасибо за инфу! Буду переваривать.-))))

Ответить   Вадим Шешунов Fri, 10 Feb 2006 23:12:47 +0200 (#513554)

 

Добрый день Вадим,

Вобщем я делал так. System Tray это toolbar с кнопками. Все что нужно
это получить список этих кнопок. Прям так сходу не смогу сказать как
это делается, MSDN нет под рукой. Что то типа GET_BUTTON мессадж
должен быть. Могу поискать проект на делфи если интересует. Эту идею в
свое время я высмотрел на CodeProject.com. Там проект был на C++
написан.

Ответить   Mon, 13 Feb 2006 11:58:18 +0200 (#514484)

 

Здравствуйте, Алексей.

Monday, February 13, 2006, 11:58:18 AM, you wrote:

Был бы Вам очень признателен за этот проект. Вообще-то, я нашел пример
на С++ и переделал его на Делфу. Получилось такое:
var
wnd,hpr :HWND;
pid :Cardinal;
n,i :Integer;
bt :PTBBUTTON;
mBt,mRect :Pointer;
r :TRect;
RBytes :DWORD;
p :PChar;
begin
wnd := FindWindowEx(0,0,'Shell_TrayWnd',0);
wnd := FindWindowEx(wnd,0,'TrayNotifyWnd',0);
wnd := FindWindowEx(wnd,0,'SysPager',0);
wnd := FindWindowEx(wnd,0,'ToolbarWindow32',0);
GetWindowThreadProcessId(wnd, pid);

hpr := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_READ, False, pid);
mBt := VirtualAllocEx(hpr, nil, SizeOf(TTBButton), MEM_COMMIT or MEM_RESERVE,
PAGE_READWRITE);
mRect := VirtualAllocEx(hpr, nil, SizeOf(TRect), MEM_COMMIT or MEM_RESERVE,
PAGE_READWRITE);

GetMem(p, 255);
lbx.Items.Clear;
n := SendMessage(wnd, TB_BUTTONCOUNT, 0, 0);
for i := 0 to n-1 do
begin
// ZeroMemory(mBt, SizeOf(mBt^));
if BOOL(SendMessage(wnd, TB_GETBUTTON, WParam(i), LParam(Bt))) then
Tag := SendMessage(wnd, TB_GETBUTTONTEXT, Bt.idCommand, LParam(p));
// if BOOL(ReadProcessMemory(hpr, mBt, @bt, SizeOf(TTBButton), RBytes))
then
// Tag := SendMessage(wnd, TB_GETBUTTONTEXT, bt.idCommand, LParam(p[0]));
end;
FreeMem(p, 255);
VirtualFreeEx(hPr, mBt, 0, MEM_RELEASE);
VirtualFreeEx(hPr, mRect, 0, MEM_RELEASE);
// close thread process handle
CloseHandle(hPr);
end;
Я тут в качестве эксперементов "поначеркивал" и поудалял кое-что по
сравнению с оригиналом. Cамое интересное то, что число кнопок в трее
определяет четко, а вот чтобы определить параметры i-ой кнопки... В
принципе, я понимаю, почему так происходит - число-то определяется по
хэндлу бара. А после, этого я начинаю "плавать", поскольку не совсем
понимаю, как добраться до этой i-й кнопки. В частности, у меня большое
удивление вызывает то, что SendMessage(wnd, TB_GETBUTTON, WParam(i),
LParam(Bt)) в Bt возвращает одно и то же для любого i.
В общем, если меня поправите, буду признателен.
Кстати, вот оригинальный текст на С. Но даже при более-менее точном
его переводе на Делфи ничего не вышло.

HWND hWnd; // my window
RECT rcTray; // my icon rect
HWND hTrayToolTip; // tray tooltip
HWND hTrayBalloon; // tray balloon
HWND hTrayToolBar; // handle to tray window

bool SetTrayToolRect(bool LocateHints)
{
// find shell tray window
HWND hShellTray = ::FindWindow("Shell_TrayWnd",NULL);
// find tray notify window
HWND hTrayNotify = ::FindWindowEx(hShellTray, NULL, "TrayNotifyWnd", NULL);
// find tray toolbar
hTrayToolBar = ::FindWindowEx(hTrayNotify, NULL, "ToolbarWindow32", NULL);

// get shell tray thread and process ids
DWORD dTrayProcess, dTrayThread;
dTrayThread = ::GetWindowThreadProcessId(hTrayToolBar, &dTrayProcess);

// locate hints
if (LocateHints)
{
// find tray balloon and tooltip
hTrayBalloon = NULL;
hTrayToolTip = NULL;
// find window
HWND hBalloon = ::FindWindowEx(NULL, NULL, TOOLTIPS_CLASS, NULL);
while (hBalloon)
{
DWORD dProcess, dThread;
dThread = ::GetWindowThreadProcessId(hBalloon, &dProcess);
if (dThread == dTrayThread && dProcess == dTrayProcess)
{
// balloon style
if (::GetWindowLong(hBalloon, GWL_STYLE) & TTS_BALLOON)
{
hTrayBalloon = hBalloon;
if (hTrayToolTip) break;
}
// tooltip
if ((::GetWindowLong(hBalloon, GWL_STYLE) & TTS_NOPREFIX) == 0)
{
hTrayToolTip = hBalloon;
if (hTrayBalloon) break;
}
}
// get next window
hBalloon = ::FindWindowEx(NULL, hBalloon, TOOLTIPS_CLASS, NULL);
}
}

// open tray process
HANDLE hTrayProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dTrayProcess);
// allocate memory in tray process
// button info
void* mButton = ::VirtualAllocEx(hTrayProcess, NULL, sizeof(TBBUTTON),
MEM_COMMIT, PAGE_READWRITE);
// rect
void* mRect = ::VirtualAllocEx(hTrayProcess, NULL, sizeof(RECT),
MEM_COMMIT, PAGE_READWRITE);
// handle to window receiving tray messages
void* mWnd = ::VirtualAllocEx(hTrayProcess, NULL, sizeof(HWND),
MEM_COMMIT, PAGE_READWRITE);
// look for the icon
bool Found = false;
// get buttons count
int nButtons = ::SendMessage(hTrayToolBar, TB_BUTTONCOUNT, 0, 0);
for (int i=0; i<nButtons; i++)
{
// reset button
TBBUTTON Button;
::ZeroMemory(&Button, sizeof(TBBUTTON));
DWORD ReadBytes = 0;
// get button
::SendMessage(hTrayToolBar, TB_GETBUTTON, i, LPARAM(mButton));
// transfer to local address space
::ReadProcessMemory(hTrayProcess, mButton, &Button, sizeof(TBBUTTON), &ReadBytes);

// get icon owner window handle - this is the trick!!! ;)
HWND hCompareWnd = NULL;
// transfer to local address space
::ReadProcessMemory(hTrayProcess, (void*)Button.dwData, &hCompareWnd, sizeof(HWND),
&ReadBytes);
// compare handles
if (hCompareWnd == hOwner)
{
// found
::ZeroMemory(&rcTrayIcon, sizeof(RECT));
// get icon rect
::SendMessage(hTrayToolBar, TB_GETRECT, Button.idCommand, LPARAM(mRect));
// convert to local space
::ReadProcessMemory(hTrayProcess, mRect, &rcTrayIcon, sizeof(RECT), &ReadBytes);
// end searching
Found = true;
break;
}
}

// free memory in tray process
::VirtualFreeEx(hTrayProcess, mButton, 0, MEM_RELEASE);
::VirtualFreeEx(hTrayProcess, mRect, 0, MEM_RELEASE);
::VirtualFreeEx(hTrayProcess, mWnd, 0, MEM_RELEASE);
// close thread process handle
::CloseHandle(hTrayProcess);

// check result
if (!Found)
{
// reset
::ZeroMemory(&rcTrayIcon, sizeof(RECT));
return false;
}

// convert to screen
::ClientToScreen(hTrayToolBar, (POINT*)(&(rcTrayIcon.left)));
::ClientToScreen(hTrayToolBar, (POINT*)(&(rcTrayIcon.right)));

// successful return
return true;
}

Ответить   Вадим Шешунов Mon, 13 Feb 2006 22:20:33 +0200 (#514796)

 

Здравствуйте, Вадим Шешунов!

[пропущено]

Обратите внимание - System tray это Toolbar с кнопками,
_находящийся_в_адресном_пространстве_трея_.
То есть, любые указатели, передаваемые Toolbar'у, должны указывать в
память трея (System tray не имеет доступа к нашей памяти).

В коде на C++ такой принцип:
- выделяем память в пространстве трея
- получаем туда нужную инфу
- копируем память себе
- освобождаем память в пространстве трея

вместо GetMem(p, 255);
должно быть p := VirtualAllocEx(hpr, nil, 255, MEM_COMMIT or MEM_RESERVE,
PAGE_READWRITE);
и т.д. С уважением, AleX

Номер выпуска : 5197
Возраст листа : 877 (дней)
Количество подписчиков : 546
Адрес в архиве : http://subscribe.ru/archive/comp.soft.prog.prog/msg/514864
Получить правила : mailto:comp.soft.prog.prog-rules@subscribe.ru
Формат "дайджест" : mailto:comp.soft.prog.prog-digest@subscribe.ru
Формат "каждое письмо" : mailto:comp.soft.prog.prog-normal@subscribe.ru
Формат "читать с веба" : mailto:comp.soft.prog.prog-webonly@subscribe.ru

Ответить   Tue, 14 Feb 2006 03:14:28 +0300 (#514864)

 

Здравствуйте, AleX.

переменных. То есть, в цикле обходится все иконки трея, возвращаются
координаты их прямоугольников.
Теперь вопрос, как определить, нужная ли это иконка. Думаю сделать это
по текстам ярлыков. Но опять запутался с методикой посылки сообщения.
Делаю так:
var
hShellTray, hTrayNotify, hTrayToolBar, hSysPager :HWND;
hBalloon, dProcess, dThread :HWND;
hpr,cwd :HWND;
dTrayProcess, dTrayThread :DWORD;
nButtons,i :Integer;
bt :TTBBUTTON;
mBt :Pointer;
mRect :^TRect;
rcTrayIcon :TRect;
RBytes :DWORD;
p :PChar;
hTrayBalloon, hTrayToolTip, hTrayProcess :HWND;
TInfo :^TOOLINFO;
begin
hShellTray := FindWindow('Shell_TrayWnd',0);
hTrayNotify := FindWindowEx(hShellTray,0,'TrayNotifyWnd',0);
hSysPager := FindWindowEx(hTrayNotify,0,'SysPager',0);
hTrayToolBar := FindWindowEx(hSysPager,0,'ToolbarWindow32',0);
dTrayThread := GetWindowThreadProcessId(hTrayToolBar, dTrayProcess);

hBalloon := FindWindowEx(0, 0, TOOLTIPS_CLASS, 0);
while BOOL(hBalloon) do
begin
dThread := GetWindowThreadProcessId(hBalloon, @dProcess);
if (dThread = dTrayThread) and (dProcess = dTrayProcess) then
begin
if BOOL(GetWindowLong(hBalloon, GWL_STYLE){ and TTS_BALLOON}) then
begin
hTrayBalloon := hBalloon;
if hTrayToolTip > 0 then
Break;
end;

if GetWindowLong(hBalloon, GWL_STYLE) and TTS_NOPREFIX = 0 then
begin
hTrayToolTip := hBalloon;
if hTrayBalloon > 0 then
Break;
end;
end;
hBalloon := FindWindowEx(0, hBalloon, TOOLTIPS_CLASS, 0);
end;
...........................................................
GetMem(p, 255);
nButtons := SendMessage(hTrayToolBar, TB_BUTTONCOUNT, 0, 0);
for i := 0 to nButtons-1 do
begin
ZeroMemory(@Bt, SizeOf(mBt));
if BOOL(SendMessage(hTrayToolBar, TB_GETBUTTON, WParam(i), LParam(mBt)))
then
begin
ReadProcessMemory(hTrayProcess, mBt, @bt, sizeof(TTBBUTTON), RBytes);
SendMessage(hTrayToolBar, TB_GETRECT, bt.idCommand, LPARAM(mRect));
ReadProcessMemory(hTrayProcess, mRect, @rcTrayIcon, sizeof(TRECT), RBytes);
ClientToScreen(rcTrayIcon.TopLeft);
//New(TInfo);
if hTrayToolTip > 0 then
begin
TInfo^.hwnd := hTrayToolBar;
TInfo^.uId := 0;//bt.idCommand;
// TInfo^.lpszText := p;
TInfo^.cbSize := SizeOf(TINFO);
if Bool(SendMessage(hTrayToolBar, TTM_GETTOOLINFO, 0, LParam(TInfo)))
then
p := '1';
end;
Dispose(TInfo);
//end;
end;
..............
end;

Проблемы начинаются в выделенном блоке. Я несовсем ясно понимаю, чем
заполняется TOOLINFO при посылке сообщения TTM_GETTOOLINFO.

Ответить   Вадим Шешунов Tue, 14 Feb 2006 18:46:44 +0200 (#515198)

 

Здравствуйте, Вадим Шешунов!

Я бы нашел HWND окна, иконку которого нужно найти, а потом
сравнивал бы HWND перебираемых иконок с найденным HWND окна.

HWND иконки в примере на C++ находится так:
// get icon owner window handle - this is the trick!!! ;)
HWND hCompareWnd = NULL;
// transfer to local address space
::ReadProcessMemory(hTrayProcess, (void*)Button.dwData, &hCompareWnd,
sizeof(HWND), &ReadBytes);
то есть в Button.dwData лежит указатель, по которому в памяти трея
хранится HWND для этой иконки.

С уважением, AleX

Номер выпуска : 5199
Возраст листа : 879 (дней)
Количество подписчиков : 544
Адрес в архиве : http://subscribe.ru/archive/comp.soft.prog.prog/msg/516003
Получить правила : mailto:comp.soft.prog.prog-rules@subscribe.ru
Формат "дайджест" : mailto:comp.soft.prog.prog-digest@subscribe.ru
Формат "каждое письмо" : mailto:comp.soft.prog.prog-normal@subscribe.ru
Формат "читать с веба" : mailto:comp.soft.prog.prog-webonly@subscribe.ru

Ответить   Wed, 15 Feb 2006 05:07:28 +0300 (#516003)

 

Здравствуйте, AleX.

Wednesday, February 15, 2006, 4:07:28 AM, you wrote:

Минутку. Разве такой фокус пройдет, если само окно, соответствующее
конкретной иконке минимизировано в трей? По крайней мере, для всех
найденных иконок IsWindow(Button.dwData) возвращает False, да и
WinSight c Spy++ окон с хэндлами Button.dwData не обнаруживают.

Ответить   Вадим Шешунов Thu, 16 Feb 2006 11:17:32 +0200 (#516215)

 

Здравствуйте, Вадим Шешунов!

И не обнаружат ;)

Button.dwData - это "пользовательские" (используемые приложением, а не
тулбаром) данные. Explorer хранит там указатель на HWND окна,
связанного с иконкой.

Теперь _внимательно_ смотрим:

В этом коде далее можно было бы использовать IsWindow(hCompareWnd)

На дельфи _наверно_ выглядело бы так:
ReadProcessMemory(hpr, Button.dwData, @hCompareWnd, SizeOf(HWND), @RBytes)

Для наглядности покажу, что мне выдал скомпилированный вчера и
немного изменённый пример на C++ (во время тестов "изменений", Explorer
"упал" раз семь-восемь, причина была найдена и устранена :) ):
01117238 00150202 0000000a
01154920 000100c6 00000009
011bcbb8 000100c6 00000008
011730e0 000100c6 00000007
0115a810 0001010a 00000006
0113b9b8 000100c6 00000005
01115458 000100c6 00000004
01115008 000100c6 00000003
0112f1e8 00010104 00000002
01126158 000200cc 00000001
01149330 000200b8 00000000
Выданные переменные: Button.dwData, hCompareWnd, Button.idCommand

В цикле перебора несколько раз выдаётся HWND 0x000100с6, у меня это
скрытое окно с именем "Connections Tray", которое как-то связано с
иконками сетевых подключений (почему в списке их шесть, мне непонятно,
больше двух подключений по сети у меня никогда не было)

PS: Вообще минимизирование (сворачивание) окна в трей, в панель задач,
в другое место - это отображение анимации свёртывания/развёртывания
окна с последующим его скрытием.
То есть, само окно не сворачивается, оно показывает анимацию, а затем
становится скрытым (невидимым), а с невидимым окном, имхо, можно
работать также как и с обычным.

С уважением, AleX

Номер выпуска : 5201
Возраст листа : 880 (дней)
Количество подписчиков : 544
Адрес в архиве : http://subscribe.ru/archive/comp.soft.prog.prog/msg/516998
Получить правила : mailto:comp.soft.prog.prog-rules@subscribe.ru
Формат "дайджест" : mailto:comp.soft.prog.prog-digest@subscribe.ru
Формат "каждое письмо" : mailto:comp.soft.prog.prog-normal@subscribe.ru
Формат "читать с веба" : mailto:comp.soft.prog.prog-webonly@subscribe.ru

Ответить   Fri, 17 Feb 2006 01:11:02 +0300 (#516998)

 

Здравствуйте, AleX.

Friday, February 17, 2006, 12:11:02 AM, you wrote:

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

Ответить   Вадим Шешунов Sat, 18 Feb 2006 17:17:42 +0200 (#517480)