Конференция "WinAPI" » Несколько вопросов по API [D7]
 
  • Виталий_____ (02.04.12 12:56) [0]
    Здравствуйте.
    Скорее всего мне потребуется ссылка на хорошую книгу либо на похожий проект с открытым кодом.
    Суть задачи. Хочу написать бота для одной игрушки. В целом, что-то типа UserBot AutoClick но с некоторыми особенностями.
    Теперь что мне требуется: так как с API мало работал, возможно вопросы будут на уровне начинающего, прошу отнестись снисходительно.
    1) Необходимо получить handle окна в котором произвели клик мышкой и, собственно, координаты.
    2) Необходим "механизм активации" заданного окна и генерации в нем клика (например, если целевое окно свернуто).
    3) И, наконец, необходима возможность получить "контент" окна в виде растра для возможного анализа.
    4) Еще одно: нужно будет повешать глобальный hot-key, работающий при неактивном окне моей программы.

    Насколько я понимаю, большинство из написанного делается одним-двумя API-вызовами. Если не трудно, приведите пример как сделать по отдельности эти 4 пункта.
  • Виталий_____ (02.04.12 14:26) [1]
    С (3) вроде сам разобрался.
    Единственное не понял, почему без задержки не работает (копирования не происходит):

    keybd_event(vk_menu,0,0,0); sleep(10);
    keybd_event(vk_snapshot,0,0,0); sleep(10);
    keybd_event(vk_snapshot,0,keyeventf_keyup,0); sleep(10);
    keybd_event(vk_menu,0,keyeventf_keyup,0); sleep(10);

  • Дмитрий С © (03.04.12 13:09) [2]
    1) FindWindow
    2) ?, PostMessage() WM_MOUSEDOWN, WM_MOUSEUP (на свернутом может и не работать)
    3) GetDC, BitBlt. Возможно WM_PRINT сработает.
    4) RegisterHotKey
  • Виталий_____ (05.04.12 06:27) [3]
    Ага, ну спасибо что хоть кто-то откликнулся.
    1) работает на ура: и кликать не надо. Под эту задачу настроил EnumWindows+ GetWindowText - выдает как раз те окна, что мне нужны.
    2) думаю напишу
    3) сейчас работает нормально, как раз переделал через BitBlt.

       ...
       ShowWindow(Wd, SW_SHOW);
       BringWindowToTop(WD);
       BitBlt(BufBitmap.Canvas.Handle, 0, 0, Width, Height, WinDC, 0, 0, SRCCOPY);


    Единственное, при развернутом на весь экран окне SW_SHOW приводит к тому что оно "восстанавливается". В моем случае это не принципиально.

    Теперь какие трудности: не смог найти такую вещь.
    Чтобы ловить нажатие мыши вне моей программы нужен глобальный хук. Пишут, что он должен быть обязательно в dll.
    Сделал как пишут, но возникла проблема: как передать данные из dll.
    Хотел передавать в dll адрес функции, которая будет принимать данные
    Hook(True,@GetData);
    ...
    procedure GetData(x:integer; y:integer);
    ...
    data[i].Pos.X:=x;
    data[i].Pos.Y:=y;
    end;



    НО при кликанье по форме проги все работает. При выходе за пределы формы (то есть когда она становится неактивной) хук срабатывает, как и должен, а вот адрес процедуры в dll сбрасывается на nil.
    Поясните, почему так и что сделать?
  • begin...end © (05.04.12 08:19) [4]
    > Виталий_____   (05.04.12 06:27) [3]

    > Чтобы ловить нажатие мыши вне моей программы нужен глобальный
    > хук. Пишут, что он должен быть обязательно в dll.

    Начиная с WinNT4 SP3 есть хук WH_MOUSE_LL, не требующий размещения в DLL.
  • Виталий_____ (05.04.12 12:34) [5]
    > begin...end ©
    Я так понимаю, xp sp2 сюда не попадает...

    Так и не понял, почему в dll неожиданно "сбрасываются" переданные указатели, будь то ссылка на процедуру или handle формы, если, собственно, форма (приложение) становится неактивным.
    Сделал передачу данных через сообщения, причем, как уже сказал, при потере фокуса handle формы тоже теряется, приходится каждый раз использовать FindWindow. Так работает.
  • begin...end © (05.04.12 12:46) [6]
    > Виталий_____   (05.04.12 12:34) [5]
    > Я так понимаю, xp sp2 сюда не попадает...

    Почему же? WinNT4 SP3, Win2000, WinXP и выше - во всех этих системах можно ставить низкоуровневый WH_MOUSE_LL-хук, и для этого вовсе не требуется писать DLL. Вот пример (писал в спешке, так что мог где-то ошибиться, но этот код работает у меня на Win7):

    type
     TTestForm = class(TForm)
       procedure FormCreate(Sender: TObject);
       procedure FormDestroy(Sender: TObject);
     private
       Hook: HHOOK;
     end;

    var
     TestForm: TTestForm;

    function HookProc(nCode, wParam, lParam: Integer): LRESULT; stdcall;
    type
     PMSLLHookStruct = ^TMSLLHookStruct;
     TMSLLHookStruct = packed record
       pt: TPoint;
       mouseData, flags, time: Cardinal;
       dwExtraInfo: PCardinal
     end;
    const
     WM_MOUSEHWHEEL = $20E;
     MsgNames: array [0..7] of string =
       ('UNKNOWN', 'WM_MOUSEMOVE', 'WM_LBUTTONDOWN', 'WM_LBUTTONUP',
        'WM_RBUTTONDOWN', 'WM_RBUTTONUP', 'WM_MOUSEWHEEL', 'WM_MOUSEHWHEEL');
    var
     I: Integer;
    begin
     if nCode = HC_ACTION then
     begin
       case WParam of
         WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_LBUTTONUP: I := WParam - WM_MOUSEMOVE + 1;
         WM_RBUTTONDOWN, WM_RBUTTONUP: I := WParam - WM_RBUTTONDOWN + 4;
         WM_MOUSEWHEEL:  I := 6;
         WM_MOUSEHWHEEL: I := 7;
      else
        I := 0
      end;
      with PMSLLHookStruct(lParam)^ do
        TestForm.Caption := Format('Message: %s  X: %d  Y: %d', [MsgNames[I], pt.X, pt.Y])
     end;
     Result := CallNextHookEx(0, nCode, wParam, lParam)
    end;

    procedure TTestForm.FormCreate(Sender: TObject);
    const
     WH_MOUSE_LL = 14;
    begin
     Hook := SetWindowsHookEx(WH_MOUSE_LL, @HookProc, HInstance, 0)
    end;

    procedure TTestForm.FormDestroy(Sender: TObject);
    begin
     UnhookWindowsHookEx(Hook)
    end

  • Виталий_____ (06.04.12 06:38) [7]
    Спасибо за код, буду иметь ввиду. Просто сути дела это не меняет:
    Из первого моего поста ясно, что задачка "развлекательная" - так, для самообразования.
    В целом, почти дописана. Хотя в планах было еще сделать анализ графики на предмет изменения картинки в заданном окне (хотя там есть анимированные подвижные элементы и задача не тривиальна, поэтому пока ограничился кликом по записанному скрипту).

    Остались одни непонятные мне грабли: при потери фокуса в моей программке "все становится ужасно": у основной формы меняется handle, в загруженной (динамически) dll пропадают переменные (переданный handle формы и так, как выяснилось, меняется, но "очищается" и строковая переменная с заголовком!):

    var buf:array[1.255] of char;
        OwnerFormCap:pchar=buf;


    То есть при вызове установки хука я передаю заголовок моей формы, все работает до тех пор как мы не кликнем за пределами самой формы (приложение потеряет фокус) и тогда мы имеем что я описал: смена handl'ов, сброс переменных. Возможно, так и должно быть - неактивное приложение, выкинули из памяти пока не станет активным... Просто раньше не сталкивался с такими граблями.
    Константную память не трогает, поэтому привязал к одному заголовку. В моем случае не страшно, но в будущем придется задумываться.
  • Сергей М. © (06.04.12 12:01) [8]

    > сути дела это не меняет


    Это радикально меняет суть дела.
  • Styx (08.04.12 23:53) [9]
    Виталий, Сергей М. Вам осторожно намекает, что, коли Вы делаете хук в dll, это значит, что данная dll загружается в адресное пространство всех процессов. Адрес @GetData имеет смысл только в адресном пространстве Вашей программы - поэтому там всё и работает; для других процессов он указывает в случайное место (возможно, не существующее).
  • Виталий_____ (09.04.12 06:52) [10]
    >Styx Что-то подобное я и ожидал, хотя осталось непонятным, почему "сбрасываются" переменные в длл - тут речь уже не о адресных а об обычном массиве символов (см. [7]).
    Собственно, дописал, кликать кликает, но

    Еще одни грабли: при смене разрешения экрана почему-то плывут координаты. Делаю так:

    //Запись:
    GetCursorPos(MousePos);
    Pos.X:=MousePos.X;
    Pos.Y:=MousePos.Y;

    ...
    //Выполнение
    px:=Round(Pos.X* (65535 / Screen.Width));
    py:=Round(Pos.Y* (65535 / Screen.Height));
    Mouse_Event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_MOVE, px, py, 0, 0);



    У меня на машинке все нормально отрабатывается, но на другом компьютере курсор ставится "немного не туда" (так и не нашел связи между соотношением разрешений и сдвигов координат). Но проблема, мне кажется, как раз в этом.
    Что тут сделать, кто знает?
  • Styx (09.04.12 15:26) [11]
    А что значит - сбрасываются? У каждого приложения эти переменные - свои. Если Вы их установили в своей программе, другие об этом ничего не знают.
  • Виталий_____ (10.04.12 07:16) [12]

    > Если Вы их установили в своей программе

    Еще раз, получается так:
    Мы загружаем dll в программе.
    Вызываем ф-ю и передаем ей pchar строку (заголовок формы прогр.).
    В функции эта строка копируется в переменную, объявленную в dll.
       var buf:array[1.255] of char;
       OwnerFormCap:pchar=buf;


    Там же устанавливается хук на мышь.
    Далее, в процедуре хука мы ищем окно программы по его заголовку.
    Так вот, при "деактивации" программы хук срабатывает как и должен, а вот OwnerFormCap (который был верно заполнен ) вдруг становится пустым.
    Не трогается константная память (как сейчас и реализовано), то есть dll привязана "к жесткому заголовку окна".
  • Виталий_____ (10.04.12 07:23) [13]
    Плохо что нельзя редактировать сообщение...
    Ctrl+c, Ctrl+v сделал и второй раз точки нету

    var buf:array[1..255] of char;
      OwnerFormCap:pchar=buf;
    Надеюсь, не раздражает читателей, я не специально :)
  • Сергей М. © (12.04.12 10:40) [14]

    > Виталий_____   (10.04.12 07:16) [12]


    В каждый из процессов, в котором произошло хук-событие, на который ты "подписался" при установке хука, будет загружен отдельный экземпляр твоей DLL.
    В каждом из этих экземпляров  - свой отдельный экземпляр переменной buf.
    А инициализировал ты только ту переменную, которая находится в загруженном тобой явно в адресное пространство твоей программулины экземпляре DLL. В остальных же потенциальных экземплярах переменная buf именно по этой причине "вдруг становится пустым"
  • Юрий Зотов © (16.04.12 15:48) [15]

    > Виталий_____   (10.04.12 07:16) [12]

    "На огурцах". На стол поставили 10 пустых тарелок, а затем в одну из них налили суп. Окажется ли суп и в остальных тарелках тоже, или они останутся пустыми?

    Тарелки - это как бы это экземпляры DLL, загруженные разными программами (точнее, это не совсем так, но ради понятности касаться тонкостей не будем). Суп - это сами данные.

    Ваша программа наполнила супом (данными) только ОДНУ из тарелок - ту DLL, которую она же и загрузила. Откуда же суп окажется в остальных?

    Они так и останутся пустыми.
  • Виталий_____ (19.04.12 13:21) [16]
    Что-то не пойму ваши комментарии: в памяти 1 экземпляр dll, загруженный одной программой (по другим соображениям ставил запрет двойного запуска).
    При всем уважении к форумчанам, совсем не понял при чем тут огурцы.
    У нас 1 программа, 1 dll и 1 хук.
    При чем тут разные экземпляры dll? То что вы говорите, мне понятно, но какое это имеет отношение к [7] или [12]?
  • tesseract © (19.04.12 14:12) [17]

    > При чем тут разные экземпляры dll?


    У них таки разное адресное пространство. Переменные никуда не сбрасываются - у каждой программы они будут своими. В общем учи матчасть по DLL, указателям и RPC.
  • Cobalt © (20.04.12 10:25) [18]
    Когда ты ставишь хук, система "дает команду" программам загрузить ДЛЛ хука.
    И в каждом процессе, на которые ты повесил хук, будет загружен свой экземпляр ДЛЛ, со своим сегментом данных и стека - в адресном пространстве (АП) того процесса.
    Соответственно, указатель в АП твоей программы на какую-нибудь структуру или строку будет корректен, а в АП другого процесса - нет.

    Это как с адресом квартиры - в твоем городе 3-я улица Строителей, дом 25, кв. 12 - это твоя квартира, а вот в другом городе...
 
Конференция "WinAPI" » Несколько вопросов по API [D7]
Есть новые Нет новых   [134430   +3][b:0][p:0.002]