Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Делфи _ Правильный WM_LButtonClick

Автор: Unconnected 19.06.2011 19:13

Как правильно отправить кнопке клик мыши? Делал традиционно так:

Procedure clickng(w:THandle);
begin
SendMessage(w, WM_LButtonDown, 1, 1);
SendMessage(w, WM_LButtonUP, 1, 1);
end;


за 1-3 параметры уверен, а вот 4й - в msdn написано, что там должна быть структура с координатами курсора, а везде в сети там тупо 0 или 1, но я подозреваю, что это очередной ГК.. хотя раньше всегда так же делал, ну вот сейчас опять работает как-то непонятно и через раз.

Автор: IUnknown 19.06.2011 19:22

Опять "магические числа"?

SendMessage(w, WM_LBUTTONDOWN, MK_LBUTTON, MakeLong(xPos, yPos));
не проще читается?

Автор: Unconnected 19.06.2011 19:25

Проще, ну это для краткости) А xPos-yPos это искать координаты кнопки ведь?

Автор: IUnknown 19.06.2011 19:31

xPos и yPos - это координаты мыши (относительно клиентской части окна W), которые будут переданы в обработчик WM_LBUTTONDOWN.

Автор: Unconnected 19.06.2011 19:32

Проще, ну это для краткости) А xPos-yPos это искать координаты кнопки ведь?

added: и ещё.. в msdn про последний параметр что-то было про "выше-левее угла клиентской области", что ли, не совсем понял. Это, случаем, значило не то, что клик не будет работать, если кнопка вне экрана (окно так расположено, например) ?

Автор: IUnknown 19.06.2011 19:34

Цитата
в msdn про последний параметр что-то было про "выше-левее угла клиентской области", что ли, не совсем понял.
Это?
Цитата(MSDN)
The coordinate is relative to the upper-left corner of the client area.
"Координата относительно верхнего левого угла клиентской области".

Автор: Unconnected 19.06.2011 20:00

Procedure clickng(w:THandle);
var r,r2:TRect;
p:TPoint;
begin
getwindowrect(w,r);
p:=r.TopLeft;
SendMessage(w, WM_LBUTTONDOWN, MK_LBUTTON, MakeLong(p.x, p.y));
SendMessage(w, WM_LBUTTONUP, MK_LBUTTON, MakeLong(p.x,p.y));
end;


Сделал так.. что-то он вообще кликать перестал, только тень на кнопке появляется.

Автор: IUnknown 19.06.2011 20:17

Следи за руками:

  p:=r.TopLeft;
ScreenToClient(w, p); // Я ж 2 раза написал - что относительно клиентской области КНОПКИ
SendMessage(w, WM_LBUTTONDOWN, MK_LBUTTON, MakeLong(p.x, p.y));
Теперь нажимается? smile.gif

Автор: Unconnected 19.06.2011 20:33

O_o нажалось... я сначала примерно так же пробовал, только ScreenToClient-ом возвращал значение в другую переменную, а там видать входной параметр сам и изменяется.. спасибо)

Автор: Unconnected 20.06.2011 4:10

deleted

Автор: TarasBer 20.06.2011 13:31

А тебе для чего посылать щелчок?
Я когда таким образон радиогруппу переключал (потому что это наименее накладный способ, не запоминать ИД первого и последнего элементов), Вольво меня разругал.

Автор: Unconnected 20.06.2011 15:53

Ну, надо кнопку нажать.. или ещё как-то можно её нажать, не щелчком? Он как-то нестабильно работает. У меня на машине всегда, а на других иногда вообще не кликает, хотя тоже XP.. посмотрите, может не так делаю чего..


const kname='PrivatCom';
var kh,but1h:THandle;
Procedure clickng(w:THandle);
var r:TRect;
p:TPoint;
begin
getwindowrect(w,r);
p:=r.TopLeft;
ScreenToClient(w, p);
sendMessage(w, WM_LBUTTONDOWN, MK_LBUTTON, MakeLong(p.x, p.y));
sendMessage(w, WM_LBUTTONUP, MK_LBUTTON, MakeLong(p.x, p.y));
end;

function GetText(wnd:THandle):string;stdcall;
var p:array [0..pred(MAX_PATH)] of char;
begin
GetWindowText(wnd,p,max_path);
result:=strpas(p);
end;

var res:THandle;
fn:string;

function ChildTree(Han:THandle; Info: lparam):BOOL;stdcall;
var sp:string;
begin
sp:=ansiuppercase(gettext(han));
if pos(fn,sp)>0 then begin
res:=han;
result:=false;
end else result:=true;
end;

function findbut(h:THandle;fnk:string):THandle; //ищет дочерние окна h, в именах которых есть fnk
begin
res:=0;fn:=fnk;
enumChildWindows(h,@ChildTree, 0);
result:=res;
end;

function findnewk(k:THandle):THandle; //может быть несколько окон с одним caption-ом, ищет новое,
var d:integer; //если его нет, ждет пока оно появится 50 с.
begin
d:=0;result:=0;
repeat
result:=findbut(0,kname);
sleep(1);inc(d);
until ((result<>k) and (result<>0)) or (d=50000);
end;

Procedure kdown;
var k:THandle;
begin
kh:=findnewk(0);
sleep(3000); //надо ли?
if kh<>0 then but1h:=findbut(kh,'ПОДКЛЮЧЕНИЕ') else exit;
clickng(but1h);
end;

Автор: TarasBer 20.06.2011 16:07

Чтобы нажать кнопку, надо просто вызвать ту же процедуру, которая сидит в ветке WM_COMMAND->ID_BTN_1 в оконной процедуре.

Автор: Unconnected 20.06.2011 16:15

Что-то новое.. и как её вызвать, тоже sendmessage какой-то?

Автор: IUnknown 20.06.2011 16:29

Цитата
посмотрите, может не так делаю чего..
Угу... Все не так... Не надо делать этот ужасный цикл длительностью до 50 секунд. Проверил один раз - нет на экране подходящего окна - все, устанавливай хук. Глобальный. На HCBT_CREATEWND. Там проверяй заголовок создаваемого окна, и если он - тот, что нужен, то работай дальше (дождись появления окна на экране и пошли ему Enter, если кнопка, которую ты пытаешься нажать - дефолтная, а в большинстве случаев это так - то она и нажмется. Если не дефолтная - то надо будет искать).

На данный момент у меня твой код не работает. По одной простой причине:
  if pos(fn,sp)>0 then begin // Ищем заголовок
, если учесть, что в fn находится заголовок, НЕ приведенный к верхнему регистру, то программа даже теоретически не может отработать. Никогда (поскольку 'PrivatCom' и 'PRIVATCOM' - это очень уж разные вещи для компьютера). Либо ты показываешь не тот код, который работает, либо выдаешь желаемое за действительное...

Автор: Unconnected 20.06.2011 16:36

Ооо нет, опять эти dll, мэппинг, затыки на пустом месте.. а почему бы не проверять хотя бы в таймере наличие нужного окна? Кнопки все дефолтные (TButton и TBitButton).

added:

Цитата
Либо ты показываешь не тот код

Да да, в оригинале большими буквами, эту константу уже тут допечатывал.. так то хэндлы всегда находятся, но нажимается далеко не всегда.

Автор: IUnknown 20.06.2011 16:43

Цитата
а почему бы не проверять хотя бы в таймере наличие нужного окна?
Да мне-то все равно, хоть вручную проверяй (показывай каждую секунду сообщение пользователю, "если на экране появилось окошко с заголовком bla_bla_bla, то подведите мышу к кнопке ПОДКЛЮЧЕНИЕ и нажмите левую кнопку мыши. Если нет - нажмите Cancel"). Только вот пользоваться такой программой никому на фиг не надо. Равно как и той, что работает по таймеру.

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

Автор: Unconnected 20.06.2011 16:58

Хочу нормально, а с дллками связываться не хочу... Ладно, допустим есть у меня хэндл кнопки\окна, чтобы отправить ей WM_COMMAND, надо ID кнопки где-то взять же?

Автор: TarasBer 20.06.2011 17:22

> Что-то новое.. и как её вызвать, тоже sendmessage какой-то?

Нет, тупо берёшь и вызываешь.

Ты ведь нажимаешь кнопку своего приложения, так? У тебя на эту кнопку уже повешена какая-то процедура, так? Ну вот её тупо и вызывай.

Автор: Unconnected 20.06.2011 17:27

Если бы своего, то понятное дело не кликал бы так) В общем, посмотрел, что окну при нажатии шлётся: извещение BN_CLICKED, в wParam лежит ID кнопки, а в lParam - её хэндл, или окна.. вот как бы ID получить.

Автор: TarasBer 20.06.2011 18:11

http://msdn.microsoft.com/en-us/library/ms645478(VS.85).aspx

Автор: IUnknown 20.06.2011 20:43

2 Unconnected: smile.gif

Смотри: (Показать/Скрыть)
А теперь - внимание, вопрос: не запуская этот код - подумай, он будет работать или нет? Чем чревато, в общем, все плюсы и минусы - в студию... У меня Delphi 2009 под WinXP, если что. Итак?

Автор: Unconnected 20.06.2011 20:45

Procedure clickng(w:THandle);
begin
postmessage(winh,WM_COMMAND,MAKEWPARAM(GetDlgCtrlID(w),BN_CLICKED),winh);
end;

так надо? Вроде не напутал с hi\low в wparam.

added: ну вообще должен работать, только при отправке WM_COMMAND в lParam должно быть Handle to the control window - я так понял, хэндл самого окна, а не кнопки..
Тут сделано две Callback-процедуры - а у меня одна, смысл её искать окно с определённым кэпшном на другом окне (то есть и окно на раб. столе найдет, и кнопку на окне).
Вообще как-то по-интересному - где-то в lParam втыкается просто хэндл, где-то с Makelparam.. А, ещё для русских строк у меня работает только ANSIUppercase.

Автор: IUnknown 20.06.2011 21:44

Цитата
только при отправке WM_COMMAND в lParam должно быть Handle to the control window - я так понял, хэндл самого окна, а не кнопки..
Да ладно... Control window - это оно и есть, окно контрола, т.е, кнопки...

Цитата
Тут сделано две Callback-процедуры - а у меня одна, смысл её искать окно с определённым кэпшном на другом окне
Смысл - в том, что не надо путать EnumWindows и EnumChildWindows. Каждый занимается своим делом: EnumWindows ищет нужную форму на десктопе, а EnumChildWindows - на найденной форме ищет дочерний контрол. Опять же, ты можешь делать как хочешь, но если ты используешь функции не по назначению - потом не удивляйся некорректному поведению программы.
Цитата
а у меня одна
А желания она не исполняет? Еду из холодильника не может достать и подогреть? Почему одна функция (заметь, CALLBACK-функция) должна решать несколько задач? Я предпочитаю "divide and conquer" ©

Цитата
Вообще как-то по-интересному - где-то в lParam втыкается просто хэндл, где-то с Makelparam
Ну, передай в PostMessage тоже с MakeLParam... Работоспособности это не меняет...

Цитата
А, ещё для русских строк у меня работает только ANSIUppercase.
У тебя это тоже присутствует, кстати. Твой AnsiUpperCase не работает для русских строк на моей машине, ибо у меня ANSI-страница - другая.

Автор: Unconnected 21.06.2011 5:50

Ок, разделяю и властвую) То есть, Uppercase далеко не универсальный? Что с этим можно сделать?.. Из-за этого опять могут вылезти непонятки, в других моментах..

Автор: IUnknown 21.06.2011 12:39

Цитата
То есть, Uppercase далеко не универсальный? Что с этим можно сделать?
Это зависит от версии компилятора. По крайней мере D2009 и выше позволяют написать:
if Pos(WideUpperCase(WideString(sCaptionToFind)), WideUpperCase(WideString(GetText(h)))) > 0 then
// ...
, и это работает, если исходник сохранен в UTF8 (я вообще взял себе за привычку все исходники сохранять в UTF8, а не в ANSI, не только дельфийские)

В принципе, можно было бы и убрать эту промежуточную конвертацию в WideString, это уже чтоб наверняка...

Автор: Unconnected 22.06.2011 5:13

Ого, а у меня D7.. короче решил не заморачиваться, сделал перекрывающую функцию, надеюсь будет на всех машинах с русским языком работать:

Function upppercase(s:string):string;
var i:integer;
begin
for i:=1 to length(s) do begin
if (s[i] in ['a'..'z']) or (s[i] in ['à'..'ÿ']) then s[i]:=chr(ord(s[i])-32);
end;
result:=s;
end;

Автор: skyjumping 1.10.2012 23:17

так то оно так..