Помощь - Поиск - Пользователи - Календарь
Полная версия: Правильный WM_LButtonClick
Форум «Всё о Паскале» > Delphi, Assembler и другие языки. > Delphi
Unconnected
Как правильно отправить кнопке клик мыши? Делал традиционно так:
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
Опять "магические числа"?

SendMessage(w, WM_LBUTTONDOWN, MK_LBUTTON, MakeLong(xPos, yPos));
не проще читается?
Unconnected
Проще, ну это для краткости) А xPos-yPos это искать координаты кнопки ведь?
IUnknown
xPos и yPos - это координаты мыши (относительно клиентской части окна W), которые будут переданы в обработчик WM_LBUTTONDOWN.
Unconnected
Проще, ну это для краткости) А xPos-yPos это искать координаты кнопки ведь?

added: и ещё.. в msdn про последний параметр что-то было про "выше-левее угла клиентской области", что ли, не совсем понял. Это, случаем, значило не то, что клик не будет работать, если кнопка вне экрана (окно так расположено, например) ?
IUnknown
Цитата
в msdn про последний параметр что-то было про "выше-левее угла клиентской области", что ли, не совсем понял.
Это?
Цитата(MSDN)
The coordinate is relative to the upper-left corner of the client area.
"Координата относительно верхнего левого угла клиентской области".
Unconnected
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
Следи за руками:

  p:=r.TopLeft;
ScreenToClient(w, p); // Я ж 2 раза написал - что относительно клиентской области КНОПКИ
SendMessage(w, WM_LBUTTONDOWN, MK_LBUTTON, MakeLong(p.x, p.y));
Теперь нажимается? smile.gif
Unconnected
O_o нажалось... я сначала примерно так же пробовал, только ScreenToClient-ом возвращал значение в другую переменную, а там видать входной параметр сам и изменяется.. спасибо)
Unconnected
deleted
TarasBer
А тебе для чего посылать щелчок?
Я когда таким образон радиогруппу переключал (потому что это наименее накладный способ, не запоминать ИД первого и последнего элементов), Вольво меня разругал.
Unconnected
Ну, надо кнопку нажать.. или ещё как-то можно её нажать, не щелчком? Он как-то нестабильно работает. У меня на машине всегда, а на других иногда вообще не кликает, хотя тоже 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
Чтобы нажать кнопку, надо просто вызвать ту же процедуру, которая сидит в ветке WM_COMMAND->ID_BTN_1 в оконной процедуре.
Unconnected
Что-то новое.. и как её вызвать, тоже sendmessage какой-то?
IUnknown
Цитата
посмотрите, может не так делаю чего..
Угу... Все не так... Не надо делать этот ужасный цикл длительностью до 50 секунд. Проверил один раз - нет на экране подходящего окна - все, устанавливай хук. Глобальный. На HCBT_CREATEWND. Там проверяй заголовок создаваемого окна, и если он - тот, что нужен, то работай дальше (дождись появления окна на экране и пошли ему Enter, если кнопка, которую ты пытаешься нажать - дефолтная, а в большинстве случаев это так - то она и нажмется. Если не дефолтная - то надо будет искать).

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

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

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

Выбирай, присоединяться к 90% "писателей кода", либо учиться, наконец, делать нормально... Как выберешь - скажешь...
Unconnected
Хочу нормально, а с дллками связываться не хочу... Ладно, допустим есть у меня хэндл кнопки\окна, чтобы отправить ей WM_COMMAND, надо ID кнопки где-то взять же?
TarasBer
> Что-то новое.. и как её вызвать, тоже sendmessage какой-то?

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

Ты ведь нажимаешь кнопку своего приложения, так? У тебя на эту кнопку уже повешена какая-то процедура, так? Ну вот её тупо и вызывай.
Unconnected
Если бы своего, то понятное дело не кликал бы так) В общем, посмотрел, что окну при нажатии шлётся: извещение BN_CLICKED, в wParam лежит ID кнопки, а в lParam - её хэндл, или окна.. вот как бы ID получить.
IUnknown
2 Unconnected: smile.gif

Смотри: (Показать/Скрыть)
А теперь - внимание, вопрос: не запуская этот код - подумай, он будет работать или нет? Чем чревато, в общем, все плюсы и минусы - в студию... У меня Delphi 2009 под WinXP, если что. Итак?
Unconnected
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
Цитата
только при отправке 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
Ок, разделяю и властвую) То есть, Uppercase далеко не универсальный? Что с этим можно сделать?.. Из-за этого опять могут вылезти непонятки, в других моментах..
IUnknown
Цитата
То есть, Uppercase далеко не универсальный? Что с этим можно сделать?
Это зависит от версии компилятора. По крайней мере D2009 и выше позволяют написать:
if Pos(WideUpperCase(WideString(sCaptionToFind)), WideUpperCase(WideString(GetText(h)))) > 0 then
// ...
, и это работает, если исходник сохранен в UTF8 (я вообще взял себе за привычку все исходники сохранять в UTF8, а не в ANSI, не только дельфийские)

В принципе, можно было бы и убрать эту промежуточную конвертацию в WideString, это уже чтоб наверняка...
Unconnected
Ого, а у меня 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
так то оно так..
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.