Вступление
Социальные сети и мессенджеры последнее время завоевали большую популярность среди пользователей интернета. Большинство пользователей (обычные пользователи) просто переписываются и обмениваются различным контентом с друзьями. Более продвинутые пользователи используют социальные сети для других целей, применяя элементы программирования. Лично я в настоящее время не сильно увлечён общением в соцсетях, по сравнению с прошлыми годами.
Одной из таких соцсетей (или мессенджера, – не знаю) было приложение «ДругВокруг». Точнее, было в моём обиходе. Стояло оно у меня на компьютере с Windows XP во времена, когда на данной ОС ещё можно было как-то сосуществовать в интернете. Это было примерно в 2016 году. Тогда как раз только начинали появляться смартфоны у моих знакомых, но у меня смартфона ещё не было.
В приложении «ДругВокруг» меня заинтересовала функция «Поиск пользователя по номеру телефона». В данный момент хотел прикрепить к статье иллюстрации с окнами, но приложение перестало работать, и я не смог в него войти: нет связи с сервером. А устанавливать новую версию или на компьютер со свежей ОС мне неохота. Тем более там, скорее всего, всё будет по-другому, не как раньше. Благодаря данной функции имелась (или имеется до сих пор) возможность, перебирая разные номера телефонов, посмотреть информацию о пользователе, если он зарегистрирован. Никаких ограничений на количество запросов программа не накладывала.
Автокликер
Одним зимним вечером, когда я сидел дома на больничном, и мне было скучно, я захотел написать такую программу, которая бы автоматизировала процесс перебора телефонных номеров из определённого списка или диапазона и забивала их в поиск, сохраняя результат в виде скриншота. Я не знал, существовали ли подобные программы по автоматизации действий. Слышал, что бывают похожие программы, которые симулируют активность пользователя на определённой веб-странице. Но для меня стояла более сложная задача: не только двигать курсором мыши и нажимать кнопки, но и анализировать изображение на экране. В качестве анализа изображения пришла идея анализировать цвета пикселей по конкретным координатам конкретного окна. Смысл простой. Нужно вбивать номер в поле поиска и нажимать кнопку «Найти». Если пользователь не найден – один вид окна, а если найден – другой. Виды окон можно анализировать с информационной точки зрения по цвету определённого пикселя. В зависимости от вида окна нужно принимать дальнейшие действия по алгоритму.
Для начала я предварительно подготовил алгоритм, проанализировав через Paint цвета и координаты пикселей. Алгоритм получился такой. Размещу я его под спойлером в текстовом виде, в каком я его подготовил изначально. Рисовать блок-схему мне неохота.
Алгоритм
-
Поместить курсор в поле ввода номера телефона:
1.1. Переместить указатель мыши в точку X312Y246.
1.2. Нажать и отпустить левую клавишу мыши.
1'. Очистить поле ввода, нажав комбинацию клавиш Ctrl+Backspase.
Поместить в буфер обмена необходимый номер телефона.
Произвести вставку, нажав комбинацию клавиш Ctrl+V.
Нажмать клавишу ENTER.
Подождать 500 мс.
Сдедать скриншот и проанализировать цвет пикселя X40Y288:
a) если R228G233B237, значит поиск завершён. Перейти к пункту 7;
b) если R212G208B200, значит поиск ещё не завершён. Перейти к пункту 5.Проанализировать цвет пикселя X200Y410:
a) если R225G225B225, значит аккаунт найден. Перейти к пункту 8;
b) если R243G243B243, значит аккаунт не найден. Перейти к пункту 1;Кликнуть по аватару найденного аккаунта:
8.1. Переместить указатель мыши в точку X67Y380.
8.2. Нажать и отпустить левую клавишу мыши.Сделать скриншот от X336Y30 размером X490Y851.
Сохранить скриншот в BMP файл с именем номера телефона.
Закрыть вкладку с информацией о найденном аккаунте:
11.1. Переместить указатель мыши в точку X487Y41.
11.2. Нажать и отпустить левую клавишу мыши.Перейти к пункту 1.
После алгоритма я приступил к разработке программы. Внимательные читатели моих статей знают, что я программировал тогда (и иногда это делаю и ныне) в Dev-C++. Мне когда-то давно подкинули эту программу, и она мне приглянулась. Пишу я программы для командной строки, создавая просто файл Си. А здесь мне предстояло сделать полноценный проект, хоть даже и консольный. Это разные функции в Dev-C++. И я потратил много времени на гугл-поиск, получая справочную информацию по всем этим делам. Предстояло освоить WinAPI функции работы с курсором мыши, кнопками мыши и клавиатуры, буфером обмена, графическими элементами окон. Никакого опыта по работе с этими функциями у меня не было. Получая теоретические знания, я поэтапно применял их на практике методом проб и ошибок.
Подробности работы программы я уже вряд ли вспомню. Просматривая свой код, я понимаю его только поверхностно. Окно, с которым я работал, имело название «ДругВокруг - Поиск». Это не просто название окна, а аргумент функции FindWindow. Затем была структура RECT. Это структура прямоугольника (как я это понимаю), в которую передаётся окно приложения функцией GetWindowRect. Также в программе встречаются другие типы данных, связанные с графикой, и их связи с вышеперечисленными структурами. Перед анализом пикселя в программном коде создаётся «виртуальное» изображение маленького участка окна в требуемом месте. Этим же способом создаётся большое изображение с результатом, но оно уже сохраняется в BMP-файл.
В результате программа с горем пополам заработала. На этапе её тестирования я исправлял некие косяки и дополнял различными мелочами. Программа перебирает 10000 номеров по последним четырём цифрам от 0000 до 9999. При этом я предварительно задаю вручную те или иные префиксы на своё усмотрение. Время выполнения программы занимало около 10 минут (учитывая задержки Sleep() в тексте программы перед некоторыми операциями). Тогда я обработал 4 префикса: 8953617____, 8953478____, 8953617____ и на 1000 номеров 89536293___. Все префиксы соответствуют номерам Орловской области мобильного оператора Теле2. На каждый такой префикс я создал одноимённую папку, куда разместил результаты работы программы – скриншоты с информацией о пользователе. По статистике получилось в среднем 185 зарегистрированных номеров из 10000 (1.85%). То есть, социальная сеть не особо популярная, хотя о популярности судить сложно – нужно куда больше критериев. Ниже я приведу несколько иллюстраций с результатами.
![Образец одной картинки 9536172187.bmp. Образец одной картинки 9536172187.bmp.](https://habrastorage.org/getpro/habr/upload_files/96d/e53/7dd/96de537dd44e7d787318b3b1e78aea06.jpg)
Спустя два года, в 2018 году, я опять вернулся к своему проекту. Неудивительно, но после обновления «Друга» моя программа перестала корректно работать: поменялись координаты и цвета требуемых элементов окна поиска. Пришлось корректировать программу. Старые участки кода я местами закомментировал. Да и в целом, процедура развёртывания информации с подробными данными о найденных пользователях стала занимать больше времени. А с увеличением числа поисковых запросов и вовсе приводило к зависанию. Поэтому я отказался от развёртывания информации, сохраняя только миниатюры – аватарки пользователей с ником. Для этого я скорректировал код ещё раз, зарезервировав старый. В новой программе я просканировал ещё несколько диапазонов на 10000 номеров – процедура сканирования сократилась до 5 минут. Ниже будет приведён под спойлером текст старого кода (где результатом служат не миниатюры, а скрины подробной информацией о пользователях) в том виде, как есть. А прямо сейчас – иллюстрации с результатами работы упрощённой версии программы.
![Образец одной картинки 89536150868_s.bmp. Образец одной картинки 89536150868_s.bmp.](https://habrastorage.org/getpro/habr/upload_files/30f/21b/b1c/30f21bb1cda848b6dff4c9347840dce7.jpg)
Текст программы (файл main.cpp).
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
/*#ifndef CAPTUREBLT
#define CAPTUREBLT (DWORD)0x40000000 /* Include layered windows */
#define CAPTUREBLT 0x40000000
//#define CLICK mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);Sleep(5);mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0)
/*inline void init(){
HWND dvkr = FindWindow(NULL,"ДругВокруг");
RECT crd;
GetWindowRect(dvkr,&crd);
}*/
inline void moveclick(int x, int y){
SetCursorPos(x,y);
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
Sleep(5);
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
}
inline void telclear(){
keybd_event(VK_CONTROL, 0, 0, 0);
keybd_event(VK_BACK, 0, 0, 0);
Sleep(5);
keybd_event(VK_BACK, 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
}
void txtpaste(char* text, RECT crd){
if(OpenClipboard(0)){
EmptyClipboard();
char* clip_data = (char*)(GlobalAlloc(GMEM_FIXED, MAX_PATH));
lstrcpy(clip_data, text);
SetClipboardData(CF_TEXT, (HANDLE)(clip_data));
LCID* lcid = (DWORD*)(GlobalAlloc(GMEM_FIXED, sizeof(DWORD)));
*lcid = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT);
SetClipboardData(CF_LOCALE, (HANDLE)(lcid));
CloseClipboard();
}
//В новом интерфейсе запретили комбинацию "CTRL+V";
//Отказывается путём работать "SHIFT+INSERT";
//Но работает вставка через контекстное меню;
//keybd_event(VK_CONTROL, 0, 0, 0);
/*keybd_event(VK_LSHIFT, 0, 0, 0);
//keybd_event(0x56, 0, 0, 0);
keybd_event(VK_INSERT, 0, 0, 0);
Sleep(5);
//keybd_event(0x56, 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_INSERT, 0, KEYEVENTF_KEYUP, 0);
//keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_LSHIFT, 0, KEYEVENTF_KEYUP, 0);*/
SetCursorPos(crd.left+455,crd.top+118);
mouse_event(MOUSEEVENTF_RIGHTDOWN,0,0,0,0);
Sleep(5);
mouse_event(MOUSEEVENTF_RIGHTUP,0,0,0,0);
SetCursorPos(crd.left+483,crd.top+220);
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
Sleep(5);
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
}
inline void find(RECT crd){
//Не работает поиск по нажатию виртуальной "ENTER" в новом интерфейсе;
//Придётся жмать мышью;
/*keybd_event(VK_RETURN, 0, 0, 0);
Sleep(5);
keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);*/
SetCursorPos(crd.left+420,crd.top+177);
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
Sleep(5);
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
}
inline int GetFilePointer(HANDLE FileHandle){
return SetFilePointer(FileHandle, 0, 0, FILE_CURRENT);
}
bool SaveBMPFile(char *filename, HDC bitmapDC, int width, int height){
bool Success=0;
HDC SurfDC=NULL;
HBITMAP OffscrBmp=NULL;
HDC OffscrDC=NULL;
LPBITMAPINFO lpbi=NULL;
LPVOID lpvBits=NULL;
HANDLE BmpFile=INVALID_HANDLE_VALUE;
BITMAPFILEHEADER bmfh;
if ((OffscrBmp = CreateCompatibleBitmap(bitmapDC, width, height)) == NULL)
return 0;
if ((OffscrDC = CreateCompatibleDC(bitmapDC)) == NULL)
return 0;
HBITMAP OldBmp = (HBITMAP)SelectObject(OffscrDC, OffscrBmp);
BitBlt(OffscrDC, 0, 0, width, height, bitmapDC, 0, 0, SRCCOPY);
if ((lpbi = (LPBITMAPINFO)(new char[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)])) == NULL)
return 0;
ZeroMemory(&lpbi->bmiHeader, sizeof(BITMAPINFOHEADER));
lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
SelectObject(OffscrDC, OldBmp);
if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, NULL, lpbi, DIB_RGB_COLORS))
return 0;
if ((lpvBits = new char[lpbi->bmiHeader.biSizeImage]) == NULL)
return 0;
if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, lpvBits, lpbi, DIB_RGB_COLORS))
return 0;
if ((BmpFile = CreateFile(filename,
GENERIC_WRITE,
0, NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL)) == INVALID_HANDLE_VALUE)
return 0;
DWORD Written;
bmfh.bfType = 19778;
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
if (!WriteFile(BmpFile, &bmfh, sizeof(bmfh), &Written, NULL))
return 0;
if (Written < sizeof(bmfh))
return 0;
if (!WriteFile(BmpFile, &lpbi->bmiHeader, sizeof(BITMAPINFOHEADER), &Written, NULL))
return 0;
if (Written < sizeof(BITMAPINFOHEADER))
return 0;
int PalEntries;
if (lpbi->bmiHeader.biCompression == BI_BITFIELDS)
PalEntries = 3;
else PalEntries = (lpbi->bmiHeader.biBitCount <= 8) ?
(int)(1 << lpbi->bmiHeader.biBitCount) : 0;
if(lpbi->bmiHeader.biClrUsed)
PalEntries = lpbi->bmiHeader.biClrUsed;
if(PalEntries){
if (!WriteFile(BmpFile, &lpbi->bmiColors, PalEntries * sizeof(RGBQUAD), &Written, NULL))
return 0;
if (Written < PalEntries * sizeof(RGBQUAD))
return 0;
}
bmfh.bfOffBits = GetFilePointer(BmpFile);
if (!WriteFile(BmpFile, lpvBits, lpbi->bmiHeader.biSizeImage, &Written, NULL))
return 0;
if (Written < lpbi->bmiHeader.biSizeImage)
return 0;
bmfh.bfSize = GetFilePointer(BmpFile);
SetFilePointer(BmpFile, 0, 0, FILE_BEGIN);
if (!WriteFile(BmpFile, &bmfh, sizeof(bmfh), &Written, NULL))
return 0;
if (Written < sizeof(bmfh))
return 0;
return 1;
}
bool ScreenCapture(int x, int y, int width, int height, char *filename){
FILE *f;
f=fopen("1.txt","w");
HWND dvkr = FindWindow(NULL,"ДругВокруг");
RECT crd;
GetWindowRect(dvkr,&crd);
HDC hDc = CreateCompatibleDC(0);
HBITMAP hBmp = CreateCompatibleBitmap(GetDC(dvkr), width, height);
SelectObject(hDc, hBmp);
BitBlt(hDc, 0, 0, width, height, GetDC(dvkr), x, y, SRCCOPY|CAPTUREBLT);
bool ret = SaveBMPFile(filename, hDc, width, height);
DeleteObject(hBmp);
BitBlt(hDc, 0, 0, 1, 1, GetDC(dvkr), 200, 410, SRCCOPY|CAPTUREBLT);
COLORREF clrf = GetPixel(hDc,1,1);
fprintf(f,"%X\n%i\t%i\t%i\t%i\t",clrf,crd.left,crd.top,crd.right,crd.bottom);
fclose(f);
return ret;
}
int main(){
HDC hDc;
HBITMAP hBmp;
HWND dvkr = FindWindow(NULL,"ДругВокруг - Поиск");
RECT crd;
GetWindowRect(dvkr,&crd);
COLORREF clrf;
int tel=0;
char str[14],name[14];
FILE *f;
f=fopen("Out.txt","r");
for(tel=3000; tel<=3999; tel++){
//while(!feof(f)){
//fscanf(f,"%s\n",&str);
sprintf(str,"8953617%04i",tel);
sprintf(name,"953617%04i.bmp",tel);
//sprintf(name,"%s.bmp",str);
//moveclick(crd.left+312,crd.top+246);
moveclick(crd.left+455,crd.top+118); //Новый интерфейс;
telclear();
//moveclick(crd.left+455,crd.top+118); //Очистили, и опять кликаем;
Sleep(5);
txtpaste(str,crd);
Sleep(10);
find(crd);
Sleep(10);
do{
Sleep(300);
hDc = CreateCompatibleDC(0);
hBmp = CreateCompatibleBitmap(GetDC(dvkr), 2, 2);
SelectObject(hDc, hBmp);
DeleteObject(hBmp);
//BitBlt(hDc, 0, 0, 1, 1, GetDC(dvkr), 40, 288, SRCCOPY|CAPTUREBLT);
//BitBlt(hDc, 0, 0, 1, 1, GetDC(dvkr), 220, 168, SRCCOPY|CAPTUREBLT);
BitBlt(hDc, 0, 0, 1, 1, GetDC(dvkr), 320, 177, SRCCOPY|CAPTUREBLT);
clrf = GetPixel(hDc,0,0);
//DeleteObject(hDc);
}while(clrf==0xC8D0D4);
//}while(clrf==0xEEEDE8);
//if(clrf!=0xEDE9E4){
//if(clrf==0xC8D0D4){
if(clrf!=0xEEEDE8&&clrf!=0xFFFFFF){
return 0;
}
//HDC hDc = CreateCompatibleDC(0);
//HBITMAP hBmp = CreateCompatibleBitmap(GetDC(dvkr), 2, 2);
//SelectObject(hDc, hBmp);
//DeleteObject(hBmp);
//DeleteObject(hDc);
//BitBlt(hDc, 0, 0, 1, 1, GetDC(dvkr), 200, 410, SRCCOPY|CAPTUREBLT);
//clrf = GetPixel(hDc,0,0);
//if(clrf==0xE1E1E1){
if(clrf==0xFFFFFF){
//fprintf(f,"%s\n",str);
//moveclick(crd.left+67,crd.top+380);
moveclick(crd.left+220,crd.top+120);
Sleep(2000);
hBmp = CreateCompatibleBitmap(GetDC(dvkr), 490, 851);
SelectObject(hDc, hBmp);
BitBlt(hDc, 0, 0, 490, 851, GetDC(dvkr), 494, 30, SRCCOPY|CAPTUREBLT); //336 -> 494;
SaveBMPFile(name, hDc, 490, 851);
DeleteObject(hBmp);
DeleteObject(hDc);
//moveclick(crd.left+487,crd.top+41);
moveclick(crd.left+646,crd.top+35);
Sleep(100);
moveclick(crd.left+440,crd.top+50);
Sleep(100);
}else{
if(clrf!=0xF3F3F3){
//return 0;
}
}
//Sleep(1000);
}
//fprintf(f,"%X\n%i\t%i\t%i\t%i\n",clrf,crd.left,crd.top,crd.right,crd.bottom);
//fclose(f);
return 0;
}
По поводу программы я, вроде бы, всё написал. Но на этом я не спешу заканчивать статью. Напишу про некие похожие процедуры, которые я проделывал в других мессенджерах.
WhatsApp и Google контакты
В то время у меня уже был смартфон и мессенджеры Viber и WhatsApp, установленные на нём. Приложение «ДругВокруг» на смартфон я не устанавливал. Наверняка там была (или есть до сих пор) функция просмотра зарегистрированных пользователей по номерам телефонов из адресной книги, что, возможно, упростила бы мою задачу. Ведь в WhatsApp, к примеру, такая функция была и есть до сих пор. Достаточно лишь загрузить в адресную книгу требуемые телефонные номера – хоть произвольные 10000 номеров из нужного диапазона, как раньше. Этим я тогда и решил заняться. Но делал я это, разумеется, не вручную. Я вспомнил, что когда-то давно в 2000-х годах, имея кнопочный телефон Siemens и COM-портовый кабель для подключения к ПК, я занимался импортом, редактированием и экспортом адресной книги. Работал я с табулированным форматом CSV в Excel. С тех пор в этом плане ничего не поменялось – стало в какой-то степени даже проще. Благодаря так называемому Google-аккаунту адресную книгу, сформированную предварительно в Excel в CSV, можно перенести в телефон. Подробности формирования книги я писать не буду, напишу кратко. Чтобы понять точный формат табуляции, я предварительно экспортировал из Google-аккаунта свою адресную книгу. Затем её просто заново отредактировал под другие номера и импортировал обратно на аккаунт. Это были те же диапазоны на 10000 номеров с фиксированным заранее выбранным префиксом и записи с именем контакта по последним четырём цифрам.
При просмотре контактов WhatsApp по это адресной книге я увидел более впечатляющую статистику: в среднем 1500 пользователей на 10000 номеров (15%). Разумеется, я смотрел только аватарки, так как иной информации в WhatsApp нет (разве что, время последнего визита). Я так же, как и в случае с «Другом», по аватарке находил своих знакомых людей, номеров которых я не знал. В среднем – 1 человек из 1000. Но у меня уже не стояла задача автоматизации сохранения найденных аватарок. Я даже не делал скриншоты вручную.
В настоящее время, насколько мне известно, политика по получению подобной информации описанными выше способами ужесточилась. Да и я перестал пользоваться вышесказанными мессенджерами ввиду отсутствия надобности. Да-да, именно так! Оказывается, вполне можно жить и без вацапов и телеграмов, вопреки иному мнению большинства. И даже фотки с телефона на телефон или ПК можно перекинуть не только без помощи вацапа, но и вовсе без помощи интернета.
Фотоколлаж из миниатюр
Когда я задумал написать эту статью, мне захотелось сделать красивые иллюстрации. В частности, необычный фотоколлаж из миниатюрных аватарок какого-нибудь одного диапазона из 10000 просканированных номеров (результат работы моей программы от 2018 г.). Коллаж я захотел получить не просто, соединив картинки, а разместив их грамотно в матрицу 100 на 100 в нужные места в зависимости от соответствующего им номера телефона. К примеру – если последние 4 цифры 1902, то фотография должна разместиться во 2-ой колонке и 19-ом ряду. А места, не соответствующие ни одному номеру, оставить пустыми (чёрными, белыми или другого цвета). Для начала я погуглил, как это сделать средствами скриптов Фотошопа или другим сторонним ПО, но ничего внятного я не нашёл. Поэтому я решил написать программу самостоятельно. Можно было написать отдельную статью на эту тему, но я напишу кратко ниже.
Для начала я в WinHex проанализировал байты моих BMP файлов с миниатюрами, полученные моей программой в 2018 г. Размер картинок – 114 на 96. Глубина пикселя тогда была выбрана не самая экономичная (но я тогда об этом даже и не думал) – 32 бита. То есть, присутствует ещё и компонент прозрачности, который везде равен 0xFF. Я решил не менять эти параметры. Новая картинка должна получиться в 100 раз больше: 11400 на 9600. Заголовок картинок миниатюр занимает 66 Байт. Палитра отсутствует, разумеется (32 бита на пиксель же). Я изучил все поля структуры заголовка, руководствуясь соответствующей информацией на Википедии. Конечно же, версий заголовков может быть много, у меня одна из них.
![Значения полей заголовка моего выходного BMP файла. Значения полей заголовка моего выходного BMP файла.](https://habrastorage.org/getpro/habr/upload_files/403/194/4cb/4031944cbde1394c71cff86eeec29913.png)
С заголовком всё понятно, это легко (допустим). А вот формирование пиксельных данных нового изображения в зависимости от имени файла исходных миниатюр и их содержимого – это ещё легче. Но это потребовало куда больше умственных усилий, чтобы не накосячить в расчётах. Сначала нужно из имени файла миниатюры получить координаты расположения в формируемой матрице. Ну, это легко. А ещё нужно получить прямую и обратную зависимости координат пикселя от смещения в файле. И следует помнить, что пиксельные данные в BMP файле хранятся построчно слева направо и снизу вверх.
В результате получился вот такой исходный код вполне рабочей программы. Для начала я не хотел прибегать к функции WriteFile, а хотел обойтись только сишной функцией fwrite. Но я не смог найти атрибут для функции fopen, чтобы открыть существующий файл для редактирования в произвольном месте. Согласно задуманному алгоритму, я сначала формирую будущий BMP файл с пустым фоном одного заранее выбранного цвета (допустим, белого), а затем уже сверху накладываю свои миниатюры по нужным местам.
Код программы для объединения фотографий.
#include <windows.h>
#include <stdio.h>
#include <string.h>
#define SIZE_X 11400
#define SIZE_Y 9600
#define SZ_X 114
#define SZ_Y 96
//#define EMPTY 0xFF000000 //Фон изображения - чёрный;
#define EMPTY 0xFFFFFFFF //Фон изображения - белый;
#define OUTFILE "8953615_100x100.bmp"
#define FILES "H:\\Devcpp_Projects\\scrgui21\\8953615____\\*.bmp"
#define CATALOG "H:\\Devcpp_Projects\\scrgui21\\8953615____\\"
HANDLE openOutputFile(const char * filename) {
return CreateFile ( filename, // Open Two.txt.
GENERIC_WRITE, // Open for writing
0, // Do not share
NULL, // No security
OPEN_ALWAYS, // Open or create
FILE_ATTRIBUTE_NORMAL, // Normal file
NULL); // No template file
}
void filepos(HANDLE f, unsigned long int p){
SetFilePointer (f, p, NULL, FILE_BEGIN);
}
#pragma pack(push,2); //Включение 16-битного выравнивания полей структуры;
struct BMP{
WORD type;
DWORD size;
WORD res1;
WORD res2;
DWORD offbits;
DWORD bisize;
DWORD W;
DWORD H;
WORD planes;
WORD bc;
DWORD comp;
DWORD si;
DWORD ppmx;
DWORD ppmy;
DWORD cu;
DWORD ci;
DWORD bmr;
DWORD bmg;
DWORD bmb;
};
#pragma pack(pop);
int main(){
DWORD ww;
BMP header;
unsigned char i,buf[68];
//Формирование заголовка;
header.type = 0x4D42;
header.size = SIZE_X*SIZE_Y*4+66;
header.res1 = 0;
header.res2 = 0;
header.offbits = 66;
header.bisize = 40;
header.W = SIZE_X;
header.H = SIZE_Y;
header.planes = 1;
header.bc = 32;
header.comp = 3;
header.si = SIZE_X*SIZE_Y*4;
header.ppmx = 0;
header.ppmy = 0;
header.cu = 0;
header.ci = 0;
header.bmr = 0x00FF0000;
header.bmg = 0x0000FF00;
header.bmb = 0x000000FF;
printf("%d\n",sizeof(header));
//Вывод дампа заголовка на экран (мне так захотелось);
memcpy(buf,&header,66);
for(i=0;i<66;i++){
printf("%02X ",buf[i]);
if(!((i+1)%16)){
printf("\n");
}
}
printf("\n");
FILE *out;
unsigned char ix,iy;
unsigned int x,y,out_x,out_y;
unsigned long int pix,os;
char name[100];
//Формирование фона;
out=fopen(OUTFILE,"wb");
fwrite(&header,66,1,out);
for(y=0;y<SIZE_Y;y++){
for(x=0;x<SIZE_X;x++){
pix=EMPTY;
fwrite(&pix,4,1,out);
}
}
fclose(out);
HANDLE outf; //Выходной файл;
outf=openOutputFile(OUTFILE);
WIN32_FIND_DATA fld; //Структура с файлом;
HANDLE hf;
hf=FindFirstFile(FILES,&fld);
do{ //Пробег по файлам в папке;
FILE *in;
sprintf(name,"%s%s",CATALOG,fld.cFileName);
iy=(fld.cFileName[7]-48)*10+(fld.cFileName[8]-48);
ix=(fld.cFileName[9]-48)*10+(fld.cFileName[10]-48);
printf("(%d, %d)\n",ix,iy);
in=fopen(name,"rb");
fseek(in,66,SEEK_SET);
for(y=0;y<SZ_Y;y++){
for(x=0;x<SZ_X;x++){
fread(&pix,4,1,in);
out_x=SZ_X*ix+x;
out_y=SZ_Y*iy+(SZ_Y-y-1);
os=((SIZE_Y-1-out_y)*SIZE_X+out_x)*4+66;
filepos(outf,os);
WriteFile(outf, &pix, 4, &ww, NULL);
}
}
fclose(in);
}while(FindNextFile(hf,&fld));
CloseHandle(outf);
system("PAUSE");
return 0;
}
Результат программы, к сожалению, меня не впечатлил: слишком маленькие миниатюры, и слишком много пустых мест. Это и следовало ожидать, но заранее я даже и не смог вообразить, как это будет выглядеть. А файл получился, чуть ли не на полгигабайта. Мало того, что он открывается не везде, он ещё не везде сжимается в JPG. Удалось сделать сжатие только в Фотошопе. А для статьи я ещё сократил картинку в размерах в 20 раз. Отдельно в менее сокращённом размере я вырезал фрагмент, который также представлю ниже.
![Объединение фотографий 8953615____ в матрицу 100 на 100 (урезанная версия картинки). Объединение фотографий 8953615____ в матрицу 100 на 100 (урезанная версия картинки).](https://habrastorage.org/getpro/habr/upload_files/3da/ddd/858/3daddd8585ce2bad3429d9e5dc5d55e5.jpg)
![Менее урезанный фрагмент предыдущей картинки. Менее урезанный фрагмент предыдущей картинки.](https://habrastorage.org/getpro/habr/upload_files/a59/5fe/74a/a595fe74a70141d4fea32e6410f50fd0.jpg)
Заключение
Честно говоря, слово «автокликер» я услышал недавно. И даже не знаю, существовали ли в то время такие программы, в которых можно было составить некий скрипт автоматизированных действий и анализа цвета пикселей. В настоящее время такие программы, вроде бы, имеются, судя по тому, что выдаёт поисковик Google. Но далеко не факт, что их можно настроить под мою хоть даже и простую задачу, фигурирующей в этой статье. Хотя, я бы всё-таки отдал предпочтение таким готовым программируемым автокликерам, чем созданию собственного.
Комментарии (8)
Zrgk
02.01.2024 17:58+5Я бы такой проект делал бы на чистых HTTP-запросах, перед этим предварительно включив сниффер и изучив какие запросы прога посылает и получает.
А так для питона есть pyautogui, очень мощный инструмент
alexmay
02.01.2024 17:58а также pywinauto, разработчик наш, проект развивается
moroz69off
02.01.2024 17:58разработчик наш
Отсюда вывод: вас > 1, вы являетесь владельцами разработчика...
(ра(3РА)бовладельцы?)...
ovsds
Autohotkey - достаточно популярный инструмент для скриптов, 20 лет с первого релиза. AC Tools - вообще тулза из 90ых, сейчас и не найти в интернете. Обе прекрасно справляются с вашей задачей.
R3EQ Автор
Спасибо! Обязательно освою, когда потребуется.
rPman
Autoit же. А в месте с утилитой aurecord (которую из-за антивирусов удалили из дистрибутива лет 10 назад) сильно упрощает процесс создания скриптов.
До того момента, как разработчики забили на win32 и начали рисовать контролы самостоятельно, можно было проанализировать окно с помощью spy++ из установки visualstudio, и используя методы win32 по работе с окнами (каждый элемент и контрол - было win32 window) можно было вытаскивать еще и текстовую информацию.
А еще, если приложение позволяет выделить и скопировать текст, проще симулировать нажатия этих кнопок и сохранять этот текст, вместо распознавания изображений
R3EQ Автор
Спасибо за инфо!
saboteur_kiev
AC Tools в свое время была просто идеальной. То, что делает автор, на AC Tools можно было за полчаса-час накидать.
К сожалению из-за отсутствия поддержки есть проблемы с координатами.
Да собственно у многих тулзов проблема с DPI и масштабированием.