ocr识别是使用tesseract来搞得,因为tesseract的编译太麻烦了,就通过system直接命令行识别了在通过读取命令行界面的字符获取结果的。
//键盘和对应按键值的映射 std::map<wchar_t, unsigned int>key_vlaue{ {L'0',0x60},{L'1',0x61},{L'2',0x62},{L'3',0x63},{L'4',0x604},{L'5',0x65},{L'6',0x66},{L'7',0x67},{L'8',0x68},{L'9',0x69} }; struct ListNode { int val; ListNode *next; ListNode() : val(0), next(nullptr) {} ListNode(int x) : val(x), next(nullptr) {} ListNode(int x, ListNode *next) : val(x), next(next) {} }; /* * 移动到指定位置并左键单击 */ void move_left_clik_mouse(int x, int y) { SetCursorPos(x, y); //鼠标移动到指定位置,print screen键很有用 INPUT inputs[2]{}; inputs[0].type = INPUT_MOUSE; //鼠标事件 inputs[0].mi.dx = x; inputs[0].mi.dy = y; inputs[0].mi.mouseData = 0; inputs[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE; inputs[0].mi.time = 0; inputs[0].mi.dwExtraInfo = GetMessageExtraInfo(); inputs[1].type = INPUT_MOUSE; //鼠标事件 inputs[1].mi.dx = x; inputs[1].mi.dy = y; inputs[1].mi.mouseData = 0; inputs[1].mi.dwFlags = MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE; inputs[1].mi.time = 0; inputs[1].mi.dwExtraInfo = GetMessageExtraInfo(); UINT uSent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT)); if (uSent != ARRAYSIZE(inputs)) { std::cout << "点击失败:" << HRESULT_FROM_WIN32(GetLastError()); } } /* * 本代码和信息按“原样”提供,不提供任何明示或暗示的保证,包括但不限于对适销性和/或特定用途适用性的暗示保证。 * FUNCTION: CaptureAnImage(HWND hWnd) * 目的:将屏幕截图捕获到窗口中,然后将其保存在 .bmp 文件中。 * 注意:此函数默认尝试创建一个名为 captureqwsx.bmp 的文件 * * 应该要避免截取到多余的地方 * * HWND hWnd:一般来说,是活动窗口的句柄 * int len_x:位图的x方向的长(像素) * int high_y:位图的y方向的高(像素) * int start_x:需要截图位置的起始点坐标的x值 * int start_y:需要截图位置的起始点坐标的y值 * const wchar_t* abs_file_name:文件名字符串指针(以null结尾) */ int CaptureAnImage(HWND hWnd, int len_x, int high_y,int start_x,int start_y,const wchar_t* abs_file_name); /* * 获取并得到ocr识别的结果 * std::wstring& out:out参数,用于保存ocr的结果 * size_t str_len:实际需要的字符串长度 */ bool get_ocr_result(std::wstring& out,size_t str_len) { system("C:\\Users\\gyj\\AppData\\Local\\Programs\\Tesseract-OCR\\tesseract D:\\工作\\temp\\2023年1月10日\\screenshot.bmp - -l eng --psm 7"); CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo{}; HANDLE consol_handle = GetStdHandle(STD_OUTPUT_HANDLE); if (GetConsoleScreenBufferInfo(consol_handle, &lpConsoleScreenBufferInfo)) { COORD coord{ 0,0 }; wchar_t buffer[32]{}; DWORD char_num{}; if (!ReadConsoleOutputCharacter(consol_handle, buffer, 4 * sizeof(TCHAR), coord, &char_num)) { std::cout << "读取结果错误:" << GetLastError(); out.clear(); return false; } else { out.clear(); for (size_t i{}; i < char_num&&out.size()<= str_len; ++i) { if (buffer[i] >= L'0' && buffer[i] <= L'9') { out.push_back(buffer[i]); } } if (out.size() < str_len) { return false; } return true; } } return false; } /* * 输入验证码 * code:验证码字符串 */ bool input_Verification_code(std::wstring& code) { move_left_clik_mouse(3300, 434); INPUT inputs[8]{}; //还必须在这儿分配好,不能使用堆 for (size_t i{}, j{}; i < code.size() * 2; i += 2,++j) { inputs[i].type = INPUT_KEYBOARD; //按下 inputs[i].ki.wVk = key_vlaue[code[j]]; inputs[i].ki.dwExtraInfo = GetMessageExtraInfo(); inputs[i+1].type = INPUT_KEYBOARD; //抬起 inputs[i+1].ki.wVk = key_vlaue[code[j]]; inputs[i+1].ki.dwFlags = KEYEVENTF_KEYUP; inputs[i+1].ki.dwExtraInfo = GetMessageExtraInfo(); } UINT uSent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT)); if (uSent != ARRAYSIZE(inputs)) { std::cout << "验证码输入失败:" << HRESULT_FROM_WIN32(GetLastError()); return false; } return true; } int main() { std::ios::sync_with_stdio(false);// 禁用同步 std::locale::global(std::locale("")); //本地化 //应该尽量避免目标位置有变动。 move_left_clik_mouse(3320, 550); HWND hWnd = GetForegroundWindow(); //获取活动窗口的句柄(因为鼠标点击过了,所以该窗口就是活动窗口) std::wstring result; size_t number_of_retries{}; //标记重复次数,大于10次之后,就停止识别 do { CaptureAnImage(hWnd, 96, 31, 3192, 489, L"D:\\工作\\temp\\2023年1月10日\\screenshot.bmp"); if (!get_ocr_result(result, 4)) { std::wcout << L"验证码识别出错\n"; system("cls"); move_left_clik_mouse(3246, 500); Sleep(100); move_left_clik_mouse(3400, 500); //移开鼠标,否则影响识别 Sleep(1000); number_of_retries++; if (number_of_retries > 10) { exit(0); } } else { std::wcout << result << L'\n'; break; } } while(true); input_Verification_code(result); Sleep(500); move_left_clik_mouse(3320, 550); } int CaptureAnImage(HWND hWnd,int len_x,int high_y, int start_x, int start_y, const wchar_t* abs_file_name){ HDC hdcScreen; HDC hdcWindow; HDC hdcMemDC = nullptr; HBITMAP hbmScreen = nullptr; BITMAP bmpScreen; DWORD dwBytesWritten = 0; DWORD dwSizeofDIB = 0; HANDLE hFile = nullptr; char* lpbitmap = nullptr; HANDLE hDIB = nullptr; DWORD dwBmpSize = 0; //检索窗口客户区的显示设备上下文句柄。 hdcScreen = GetDC(nullptr); hdcWindow = GetDC(hWnd); // 创建一个兼容的 DC,它在来自窗口 DC 的 BitBlt 中使用。 hdcMemDC = CreateCompatibleDC(hdcWindow); if (!hdcMemDC) { MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK); goto done; } /* 获取客户区进行尺寸计算。 RECT rcClient; GetClientRect(hWnd, &rcClient); 这是最好的拉伸模式。 SetStretchBltMode(hdcWindow, HALFTONE); 源DC是整个屏幕,目标DC是当前窗口(HWND)。 StretchBlt 函数将一个位图从源矩形复制到目标矩形中,并拉伸或压缩位图以适应目标矩形的尺寸(如有必要)。 系统根据当前在目标设备上下文中设置的拉伸模式拉伸或压缩位图。 if (!StretchBlt(hdcWindow, 0, 0, rcClient.right, rcClient.bottom, hdcScreen, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SRCCOPY)) { MessageBox(hWnd, L"StretchBlt has failed", L"Failed", MB_OK); goto done; }*/ // 从 Window DC 创建一个兼容的位图。 hbmScreen = CreateCompatibleBitmap(hdcWindow, len_x, high_y); if (!hbmScreen) { MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK); goto done; } // 选择兼容位图到兼容内存DC。 SelectObject(hdcMemDC, hbmScreen); // 位块传输到我们的兼容内存 DC。 if (!BitBlt(hdcMemDC, 0, 0, len_x, high_y, //避免边缘部分导致识别错误 //100,200, hdcWindow, start_x, start_y, SRCCOPY)) { MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK); goto done; } // 从 HBITMAP 中获取 BITMAP。 GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen); BITMAPFILEHEADER bmfHeader; BITMAPINFOHEADER bi; bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = bmpScreen.bmWidth; bi.biHeight = bmpScreen.bmHeight; bi.biPlanes = 1; bi.biBitCount = 32; bi.biCompression = BI_RGB; bi.biSizeImage = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrUsed = 0; bi.biClrImportant = 0; dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight; // 从 32 位 Windows 开始,GlobalAlloc 和 LocalAlloc 被实现为使用进程默认堆的句柄调用 HeapAlloc 的包装函数。 因此,GlobalAlloc 和 LocalAlloc 的开销比 HeapAlloc 大。 hDIB = GlobalAlloc(GHND, dwBmpSize); lpbitmap = (char*)GlobalLock(hDIB); // 从位图中获取“位”,并将它们复制到 lpbitmap 指向的缓冲区中。 GetDIBits(hdcWindow, hbmScreen, 0, (UINT)bmpScreen.bmHeight, lpbitmap, (BITMAPINFO*)&bi, DIB_RGB_COLORS); // 创建一个文件,这是我们将保存屏幕截图的地方。 hFile = CreateFile(//L"captureqwsx.bmp", abs_file_name, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (INVALID_HANDLE_VALUE == hFile) { std::cout << "文件句柄创建失败:" << GetLastError(); } // 将标头的大小添加到位图的大小以获得总文件大小。 dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); // 偏移到实际位图位开始的位置。 bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); // 文件的大小。 bmfHeader.bfSize = dwSizeofDIB; // 对于位图,bfType 必须始终为 BM。 bmfHeader.bfType = 0x4D42; // BM. WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, nullptr); WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, nullptr); WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, nullptr); // 从堆中解锁并释放 DIB。 GlobalUnlock(hDIB); GlobalFree(hDIB); // 关闭已创建文件的句柄。 CloseHandle(hFile); // 清理。 done: DeleteObject(hbmScreen); DeleteObject(hdcMemDC); ReleaseDC(nullptr, hdcScreen); ReleaseDC(hWnd, hdcWindow); return 0; }View Code
这一堆主要的功能是,识别验证码并输入后登陆系统。
标签:inputs,ListNode,鼠标,单击,int,mi,识图,next,ocr From: https://www.cnblogs.com/love-DanDan/p/17160585.html