本人最近编写了一款QQ斗地主的刷分工具,现将关键代码附上来(代码中有注释),以供参考。本人新手,请大侠切勿耻笑。成品软件可以点击此处下载:http://yunpan.cn/QtVWNVy2iIwqt
// 获取指定位置的屏幕颜色
COLORREF CQQGameAddInDlg::GetColorAtPos(int nX, int nY)
{
HDC hdcSrc = ::GetDC(s_hwndQQGame);
if (NULL == hdcSrc)
{
AfxMessageBox(_T("获取设备上下文失败"));
GetDlgItem(IDC_BUTTON2)->SendMessage(BM_CLICK, 0, 0);
return -1;
}
int nBPP = GetDeviceCaps(hdcSrc, BITSPIXEL) * GetDeviceCaps(hdcSrc, PLANES);
CRect rect;
::GetClientRect(s_hwndQQGame, &rect);
CImage image;
if (!image.Create(rect.Width(), rect.Height(), nBPP))
{
AfxMessageBox(_T("创建图像对象失败"));
::ReleaseDC(s_hwndQQGame, hdcSrc);
GetDlgItem(IDC_BUTTON2)->SendMessage(BM_CLICK, 0, 0);
return -1;
}
BitBlt(image.GetDC(), 0, 0, rect.Width(), rect.Height(), hdcSrc, rect.left, rect.top, SRCCOPY);
COLORREF color = image.GetPixel(nX, nY);
if (-1 == color)
{
ReportLog(_T("获取颜色值失败:%u\n"), GetLastError());
image.ReleaseDC();
image.Destroy();
::ReleaseDC(s_hwndQQGame, hdcSrc);
return -1;
}
::ReleaseDC(s_hwndQQGame, hdcSrc);
image.ReleaseDC();
image.Destroy();
return color;
}
void CQQGameAddInDlg::OnTimer(UINT_PTR nIDEvent)
{
// TODO: Add your message handler code here and/or call default
switch (nIDEvent)
{
case FAST_START_GAME_TIMER_ID:
{
if (s_colorFastStart == GetColorAtPos(s_nFastStartX, s_nFastStartY))
{
KillTimer(FAST_START_GAME_TIMER_ID);
ClickAtPos(s_nFastStartX, s_nFastStartY);
TCHAR szTime[9] = {0};
_strtime(szTime);
ReportLog(_T("快速开始游戏 %s\n"), szTime);
Sleep(REST_TIME_AFTER_SEND_COMMAND);
SetTimer(FAST_START_GAME_TIMER_ID, TIMER_INTERVAL, NULL);
}
}
break;
case START_GAME_TIMER_ID:
{
COLORREF colorStart = GetColorAtPos(s_nStartX, s_nStartY);
BOOL bStart = s_colorStart == colorStart;
// 主账户多增加了两个判断
if (!bStart && s_nBrowserType == 0)
{
bStart = s_colorMajorStart1 == colorStart;
if (!bStart)
{
bStart = s_colorMajorStart2 == colorStart;
}
}
if (bStart)
{
KillTimer(START_GAME_TIMER_ID);
if (BST_CHECKED == ((CButton *)GetDlgItem(IDC_CHECK1))->GetCheck())
{
// 主账户计时
if (0 == s_timeStartGame)
{
time(&s_timeStartGame);
}
else
{
time_t timeStartGame = 0;
time(&timeStartGame);
CTime time(timeStartGame - s_timeStartGame);
s_timeStartGame = timeStartGame;
CString strTimeDuration = time.FormatGmt(_T("上一局牌持续时间:%H:%M:%S\n"));
ReportLog(strTimeDuration);
}
}
ClickAtPos(s_nStartX, s_nStartY);
TCHAR szTime[9] = {0};
_strtime(szTime);
ReportLog(_T("开始第%d局游戏 %s\n"), ++s_nPKTimes, szTime);
Sleep(REST_TIME_AFTER_SEND_COMMAND);
SetTimer(START_GAME_TIMER_ID, TIMER_INTERVAL, NULL);
KillTimer(TUOGUAN_TIMER_ID); // 叫分前禁用托管
}
else
{
TRACE(_T("开始游戏的颜色为:%ul\n"), colorStart);
}
}
break;
case SANFEI_TIMER_ID:
{
if (s_colorSanfei == GetColorAtPos(s_nSanFeiX, s_nSanFeiY))
{
KillTimer(SANFEI_TIMER_ID);
ClickAtPos(s_nSanFeiX, s_nSanFeiY);
TCHAR szTime[9] = {0};
_strtime(szTime);
ReportLog(_T("三分: %s\n"), szTime);
Sleep(REST_TIME_AFTER_SEND_COMMAND);
SetTimer(SANFEI_TIMER_ID, TIMER_INTERVAL, NULL);
if (BST_CHECKED == ((CButton *)GetDlgItem(IDC_CHECK1))->GetCheck())
{
// 主账号开始托管
SetTimer(TUOGUAN_TIMER_ID, TIMER_INTERVAL, NULL);
}
SetTimer(SANFEI_TIMER_ID, TIMER_INTERVAL, NULL);
}
}
break;
case BUCHU_TIMER_ID:
{
if (s_colorBuChu1 == GetColorAtPos(s_nBuChuX, s_nBuChuY) || s_colorBuChu2 == GetColorAtPos(s_nBuChuX, s_nBuChuY))
{
KillTimer(BUCHU_TIMER_ID);
ClickAtPos(s_nBuChuX, s_nBuChuY);
TCHAR szTime[9] = {0};
_strtime(szTime);
ReportLog(_T("不出: %s\n"), szTime);
Sleep(REST_TIME_AFTER_SEND_COMMAND);
SetTimer(BUCHU_TIMER_ID, TIMER_INTERVAL, NULL);
}
}
break;
case BUJIAO_TIMER_ID:
{
if (s_colorBuJiao == GetColorAtPos(s_nBuJiaoX, s_nBuJiaoY))
{
KillTimer(BUJIAO_TIMER_ID);
ClickAtPos(s_nBuJiaoX, s_nBuJiaoY);
TCHAR szTime[9] = {0};
_strtime(szTime);
ReportLog(_T("不叫: %s\n"), szTime);
Sleep(REST_TIME_AFTER_SEND_COMMAND);
SetTimer(BUJIAO_TIMER_ID, TIMER_INTERVAL, NULL);
}
}
break;
case TUOGUAN_TIMER_ID:
{
if (s_colorTuoGuan == GetColorAtPos(s_nTuoGuanX, s_nTuoGuanY))
{
KillTimer(TUOGUAN_TIMER_ID);
ClickAtPos(s_nTuoGuanX, s_nTuoGuanY);
TCHAR szTime[9] = {0};
_strtime(szTime);
ReportLog(_T("托管: %s\n"), szTime);
Sleep(REST_TIME_AFTER_SEND_COMMAND);
}
}
break;
case LIANSHENG_TIMER_ID:
{
if (s_colorLianSheng == GetColorAtPos(s_nLianShengX, s_nLianShengY))
{
KillTimer(LIANSHENG_TIMER_ID);
ClickAtPos(s_nLianShengX, s_nLianShengY);
TCHAR szTime[9] = {0};
_strtime(szTime);
ReportLog(_T("连剩五局: %s\n"), szTime);
Sleep(REST_TIME_AFTER_SEND_COMMAND);
SetTimer(LIANSHENG_TIMER_ID, TIMER_INTERVAL, NULL);
}
}
break;
case TUO_GUAN_BUG_TIMER_ID:
{
// 托管时不出牌时的BUG
if (s_colorTuoGuanBug == GetColorAtPos(s_nTuoGuanBugX, s_nTuoGuanBugY))
{
KillTimer(TUO_GUAN_BUG_TIMER_ID);
KillTimer(TUOGUAN_TIMER_ID); // 取消托管
Beep(32767, 1000);
TCHAR szTime[9] = {0};
_strtime(szTime);
ReportLog(_T("托管不出牌BUG: %s\n"), szTime);
Sleep(REST_TIME_AFTER_SEND_COMMAND);
ClickAtPos(s_nTuoGuanBugQuXiaoTuoGuanX, s_nTuoGuanBugQuXiaoTuoGuanY); // 取消托管
ReportLog(_T("取消托管\n"));
Sleep(REST_TIME_AFTER_SEND_COMMAND);
ClickAtPos(s_nTuoGuanBugClickX, s_nTuoGuanBugClickX); // 选牌
ReportLog(_T("选牌\n"));
Sleep(REST_TIME_AFTER_SEND_COMMAND);
ClickAtPos(s_nTuoGuanBugChuPaiX, s_nTuoGuanBugChuPaiY); // 出牌
ReportLog(_T("出牌\n"));
Sleep(REST_TIME_AFTER_SEND_COMMAND);
SetTimer(TUO_GUAN_BUG_TIMER_ID, TIMER_INTERVAL, NULL);
SetTimer(TUOGUAN_TIMER_ID, TIMER_INTERVAL, NULL); // 恢复托管
}
}
break;
case PREVIEW_GAEM_TIME_ID:
{
KillTimer(PREVIEW_GAEM_TIME_ID);
CRect rectClient;
GetClientRect(&rectClient);
CRect rectRedt;
m_redtLog.GetWindowRect(&rectRedt);
ScreenToClient(&rectRedt);
rectClient.top = rectRedt.bottom;
InvalidateRect(&rectClient);
SetTimer(PREVIEW_GAEM_TIME_ID, TIMER_INTERVAL, NULL);
}
break;
}
CDialogEx::OnTimer(nIDEvent);
}
// 指定位置单击
int CQQGameAddInDlg::ClickAtPos(int nX, int nY)
{
::SendMessage(s_hwndQQGame, WM_LBUTTONDOWN, MK_LBUTTON, (nY << 16) + nX);
::SendMessage(s_hwndQQGame, WM_LBUTTONUP, 0, (nY << 16) + nX);
return 0;
int nX1 = 0, nY1 = 0; // 转化后的坐标
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
nX1 = nX * (65535. / nScreenWidth );
nY1 = nY * (65535. / nScreenHeight);
mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, nX1, nY1, 0, NULL);
mouse_event(MOUSEEVENTF_LEFTDOWN, nX1, nY1, 0, NULL);
mouse_event(MOUSEEVENTF_LEFTUP, nX1, nY1, 0, NULL);
return 0;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)