Вступление
Социальные сети и мессенджеры последнее время завоевали большую популярность среди пользователей интернета. Большинство пользователей (обычные пользователи) просто переписываются и обмениваются различным контентом с друзьями. Более продвинутые пользователи используют социальные сети для других целей, применяя элементы программирования. Лично я в настоящее время не сильно увлечён общением в соцсетях, по сравнению с прошлыми годами.
Одной из таких соцсетей (или мессенджера, – не знаю) было приложение «ДругВокруг». Точнее, было в моём обиходе. Стояло оно у меня на компьютере с 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%). То есть, социальная сеть не особо популярная, хотя о популярности судить сложно – нужно куда больше критериев. Ниже я приведу несколько иллюстраций с результатами.

Спустя два года, в 2018 году, я опять вернулся к своему проекту. Неудивительно, но после обновления «Друга» моя программа перестала корректно работать: поменялись координаты и цвета требуемых элементов окна поиска. Пришлось корректировать программу. Старые участки кода я местами закомментировал. Да и в целом, процедура развёртывания информации с подробными данными о найденных пользователях стала занимать больше времени. А с увеличением числа поисковых запросов и вовсе приводило к зависанию. Поэтому я отказался от развёртывания информации, сохраняя только миниатюры – аватарки пользователей с ником. Для этого я скорректировал код ещё раз, зарезервировав старый. В новой программе я просканировал ещё несколько диапазонов на 10000 номеров – процедура сканирования сократилась до 5 минут. Ниже будет приведён под спойлером текст старого кода (где результатом служат не миниатюры, а скрины подробной информацией о пользователях) в том виде, как есть. А прямо сейчас – иллюстрации с результатами работы упрощённой версии программы.

Текст программы (файл 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 файле хранятся построчно слева направо и снизу вверх.
В результате получился вот такой исходный код вполне рабочей программы. Для начала я не хотел прибегать к функции 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 раз. Отдельно в менее сокращённом размере я вырезал фрагмент, который также представлю ниже.


Заключение
Честно говоря, слово «автокликер» я услышал недавно. И даже не знаю, существовали ли в то время такие программы, в которых можно было составить некий скрипт автоматизированных действий и анализа цвета пикселей. В настоящее время такие программы, вроде бы, имеются, судя по тому, что выдаёт поисковик Google. Но далеко не факт, что их можно настроить под мою хоть даже и простую задачу, фигурирующей в этой статье. Хотя, я бы всё-таки отдал предпочтение таким готовым программируемым автокликерам, чем созданию собственного.
