QQ美女找茬辅助器制作

来源:互联网 发布:金字招牌餐饮软件 编辑:程序博客网 时间:2024/06/11 09:55

 

最近,有朋友总要跟我PK QQ美女找茬,无奈在下眼力实在是不如人。不过,咱可是计算机专业的啊,自己找不过他,还不能利用计算机来找吗?嘿嘿,于是开始研究这个辅助工具。

首先,先看看截图:

QQ美女找茬辅助器

下面说说制作的方法。我想,大家应该也能想通制作的方法:获取窗口句柄->找到图片(两张)->对比->设置成不同的颜色->显示出来。

过程是很简单的,先看看第一步,获取窗口句柄

Code:
  1. // 获取游戏句柄   
  2. bool CMain::GetGameHandle(void)   
  3. {   
  4.     m_pGame = FindWindow(NULL, _T("大家来找茬"));   
  5.     if(m_pGame == NULL)   
  6.         return false;   
  7.     else  
  8.         return true;   
  9. }  

这里用到了一个FindWindow()函数,函数原型如下

HWND FindWindow(LPCSTR lpClassName,LPCSTR lpWindowName);

利用窗口名称获取到一个窗口HWND.

然后,很重要的就是获取图片了,把图片要先存入一个缓冲区,以下是代码:

Code:
  1. // 将游戏中位图数据复制到内存中   
  2. bool CMain::CopyPicToBlt(DWORD*& lpvBits,  int xSrc, int ySrc, int nWidth, int nHeight)   
  3. {   
  4.         HWND hWnd = m_pGame;//主游戏句柄   
  5.   
  6.         HDC hSrcDC=NULL;   
  7.         HDC hNewDC;   
  8.         //图片格式信息头   
  9.         BITMAPINFOHEADER   bi;   
  10.         bi.biSize = sizeof(BITMAPINFOHEADER);       
  11.         bi.biWidth = nWidth;       
  12.         bi.biHeight = nHeight;     
  13.         bi.biPlanes = 1;       
  14.         bi.biBitCount = 32;       
  15.         bi.biCompression = BI_RGB;       
  16.         bi.biSizeImage = nWidth*nHeight;     
  17.         bi.biXPelsPerMeter = 0;       
  18.         bi.biYPelsPerMeter = 0;       
  19.         bi.biClrUsed = 0;       
  20.         bi.biClrImportant = 0;   
  21.   
  22.         HBITMAP pBitmap;   
  23.         hSrcDC=GetDC(hWnd);//获取程序DC   
  24.         hNewDC = CreateCompatibleDC(hSrcDC);//创建兼容DC   
  25.         pBitmap = CreateCompatibleBitmap(hSrcDC, nWidth, nHeight);//设置图大小   
  26.         SelectObject(hNewDC, pBitmap);  //绑定图片   
  27.   
  28.         //将位图复制到DC中   
  29.         BitBlt(hNewDC, 0, 0, nWidth, nHeight, hSrcDC, xSrc, ySrc, SRCCOPY);    
  30.         //为图片申请一块内存空间   
  31.         if(lpvBits)   
  32.         {   
  33.             delete[] lpvBits;   
  34.             lpvBits=NULL;   
  35.         }   
  36.            
  37.         lpvBits = new DWORD[nWidth*nHeight];   
  38.         if(!lpvBits)   
  39.         {   
  40.             return false;   
  41.         }   
  42.         //将图片数据存储到对应的变量中   
  43.         GetDIBits(hNewDC, pBitmap, 0, (UINT)m_nPicHeight, lpvBits, (BITMAPINFO *)&bi, DIB_RGB_COLORS);   
  44.         DeleteObject(pBitmap);   
  45.         DeleteDC(hNewDC);   
  46.         return true;   
  47. }  

这里,要先简单说下BMP图片的知识。BMP图片其实在计算机里也是以二进制的方式存储的(任何文件其实都是),这样,我们用一个DWORD指针来作为BUFFER,方便比较。通过这个函数,就把窗口中(xSrc, ySrc)位置的(nWidth, nHeight)大小的位图存储到了lpvBits指向的缓冲区了,以后直接比较两个缓冲区的内容即可。

Code:
  1. // 比较两个图片   
  2.   
  3. void CMain::CompareBMP(DWORD*& pBuffer, DWORD* pLeft, DWORD* pRight)   
  4. {   
  5.     if(!pBuffer)   
  6.     {   
  7.         delete[] pBuffer;   
  8.         pBuffer=NULL;   
  9.     }   
  10.     pBuffer = new DWORD[m_nPicWidth*m_nPicHeight];   
  11.     //比较两幅图,数据相同的设置为白色,不同的为红色。   
  12.     for (DWORD i = 0; i < (DWORD)m_nPicWidth * m_nPicHeight; i++)   
  13.     {   
  14.         if(pLeft[i] != pRight[i])    
  15.             pBuffer[i] = RGB(0,0,255);    
  16.         else  
  17.             pBuffer[i] = RGB(255,255,255);    
  18.     }   
  19.     //SaveBMP(pBuffer, _T("d://result1.bmp") );   
  20.     //转换图片数据,使图片按正常顺序显示。(BMP格式与窗口图像存放竖坐标相反)   
  21.     int width = m_nPicWidth;   
  22.     for (DWORD i = 0; i < (DWORD)(m_nPicHeight / 2); i++)   
  23.     {   
  24.         for (DWORD j = 0; j < (DWORD)width; j++)   
  25.         {   
  26.             DWORD temp;   
  27.             temp = *(pBuffer + i * width + j);   
  28.             *(pBuffer + i * width + j) = *(pBuffer + width * (m_nPicHeight - 1 - i) + j);   
  29.             *(pBuffer + width * (m_nPicHeight - 1 - i) + j) = temp;   
  30.         }   
  31.     }   
  32.     //SaveBMP(pBuffer, _T("d://result2.bmp") );   
  33.     if(pLeft)   
  34.     {   
  35.         delete[] pLeft;   
  36.         pLeft = NULL;   
  37.     }   
  38.     if(pRight)   
  39.     {   
  40.         delete[] pRight;   
  41.         pRight = NULL;   
  42.     }   
  43. }  

最后通过上面的来比较图片。当然,这个算法是最简单的算法,就是利用循环,按顺序比较,再将结果填充到一个新的缓冲区里,把原先的两张图片缓冲区内内容清除掉

然后,就是显示出来了,首先要先创建一个半透明的对话框

Code:
  1. // 点击查找后   
  2. void CFindFaultDlg::FindFault(void)   
  3. {   
  4.     if(!m_GameMain.GetGameHandle())   
  5.     {   
  6.         MessageBox( _T("请先打开'QQ美女找茬'游戏"), _T("温情提示"));   
  7.         return;   
  8.     }   
  9.     DWORD* pBuffer=NULL;   
  10.        
  11.     //创建一个对话框,用于显示找茬游戏中两幅图的不同点   
  12.     if(m_pDlg)   
  13.         m_pDlg->DestroyWindow();   
  14.     m_GameMain.FindingFault(pBuffer);   
  15.     m_pDlg = new CDialog();   
  16.     if(m_pDlg)   
  17.     {   
  18.         if(!m_pDlg->Create(IDD_DLGSHOW, this))    
  19.         {   
  20.             MessageBox( _T("对话框初始化失败"), _T("温情提示") );   
  21.             return ;   
  22.         }   
  23.            
  24.         //利用SetLayeredWindowAttributes设置窗口透明。   
  25.         //自带的SDK不支持,需要更新才能直接使用这个函数。   
  26.         //SetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE,GetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE)^WS_EX_LAYERED);    
  27.         //SetLayeredWindowAttributes(this->m_hWnd,0,128,2);    
  28.         //   
  29.         //动态的从User32.dll中取得SetLayeredWindowAttributes函数地址。WS_EX_LAYERED = 0x80000   
  30.         SetWindowLong(m_pDlg->GetSafeHwnd(),GWL_EXSTYLE, GetWindowLong(m_pDlg->GetSafeHwnd(),GWL_EXSTYLE)^0x80000| WS_EX_TRANSPARENT);    
  31.         HINSTANCE hInst=LoadLibrary(_T("User32.DLL"));    
  32.         if(hInst)    
  33.         {    
  34.             typedef BOOL  (WINAPI * MYFUNC)(HWND,COLORREF,BYTE,DWORD);    
  35.             MYFUNC fun=NULL;    
  36.                
  37.             //取得SetLayeredWindowAttributes函数指针    
  38.             fun=(MYFUNC)GetProcAddress(hInst,"SetLayeredWindowAttributes");    
  39.             if(fun)    
  40.                 fun(m_pDlg->GetSafeHwnd(),0,128,2);    
  41.             FreeLibrary(hInst);    
  42.         }    
  43.            
  44.         //移动该对话框窗口,置顶,覆盖找茬游戏界面中右边的那幅图。   
  45.         CRect rect;   
  46.         CWnd* pGameWnd = FindWindow(NULL, _T("大家来找茬"));   
  47.         pGameWnd->GetClientRect(&rect);   
  48.         pGameWnd->ClientToScreen(&rect);   
  49.         if(!m_pDlg->SetWindowPos(&wndTopMost, rect.left + m_ShowOffsetX, rect.top + m_ShowOffsetY,   
  50.             497, 448, SWP_SHOWWINDOW))    
  51.         {   
  52.             MessageBox(_T("设置对话框失败!"), _T("温情提示"));   
  53.             return;   
  54.         }   
  55.     }      
  56.     else  
  57.     {   
  58.         MessageBox(_T("创建对话框对象失败!"), _T("温情提示"));   
  59.         return;   
  60.     }   
  61.     //显示不同   
  62.     CDC* pShow = m_pDlg->GetDC();   
  63.     ShowFault(pShow,pBuffer);   
  64.     //m_GameMain.SaveBMP(pBuffer, _T("D://result3.bmp") );   
  65.     if(pBuffer)   
  66.     {   
  67.         delete[] pBuffer;   
  68.         pBuffer = NULL;   
  69.     }      
  70. }   

上面的函数不仅显示了对话框,而且将位置通过SetWindowPos()调整到当前游戏窗口中图片的上方

然后就是把不同的地方显示出来

Code:
  1. // 显示不同的地方   
  2. void CFindFaultDlg::ShowFault(CDC* pDC, DWORD* pBuffer)   
  3. {   
  4.     CBitmap bm;   
  5.     bm.CreateBitmap(m_GameMain.GetPicFrame().x,m_GameMain.GetPicFrame().y,1,32, pBuffer);   
  6.     //bm.LoadBitmap(_T("I://result.bmp"));   
  7.     CBrush brush;   
  8.     brush.CreatePatternBrush(&bm);   
  9.        
  10.     CBrush* pOldBrush = (CBrush*)pDC->SelectObject(&brush);   
  11.        
  12.     pDC->FillRect(&CRect(0, 0, m_GameMain.GetPicFrame().x,m_GameMain.GetPicFrame().y),&brush);   
  13.        
  14.     pDC->SelectObject(pOldBrush);   
  15.        
  16.     brush.DeleteObject();   
  17.     bm.DeleteObject();   
  18.     ReleaseDC(pDC);   
  19. }  

这里是用刚才比较结束后的缓冲区内容的位图信息创建了一个画刷brush,然后用该画刷填充整个窗口,这样就结束了。

后记:通过制作这个小小的辅助工具,了解了获取句柄的方法,遇到了许多问题,比如32位位图和24位位图就有很大区别。

还有半透明窗口的显示。而且,最后遇到了一个很诡异的问题,这个程序到这里在我自己的电脑上都很正确,可是当我在我家的电脑上使用的时候,就总是在中间多出来一个方块的东西,而且只能显示一次,再点显示也是无效,无奈在家电脑也装上VS来调试,调试的时候也没发现什么问题,该执行的语句都执行了。如果有哪位高手知道这个问题的答案,还望多多指教。顺便说一句,我的系统是Win7,家中的是Vista。

为了解决这个问题,我最后采用了另外一种方法:不是通过截取窗口,而是截取屏幕,结果就正常了。。。

Code:
  1. // 通过截屏来获取图像   
  2. bool CMain::GetPicByCap(DWORD*& lpvbits, int xSrc, int ySrc, int nWidth, int nHeight)   
  3. {   
  4.     HWND hWnd = m_pGame;    //得到句柄   
  5.     HDC hdcScreen;   
  6.     HDC hdcWindow;   
  7.     HDC hdcMemDC = NULL;   
  8.     HBITMAP hbmScreen = NULL;   
  9.     BITMAP bmpScreen;   
  10.   
  11.     // Retrieve the handle to a display device context for the client    
  12.     // area of the window.    
  13.     hdcScreen = GetDC(NULL);   
  14.     hdcWindow = GetDC(hWnd);   
  15.     hdcMemDC = CreateCompatibleDC(hdcWindow);    
  16.   
  17.     if(!hdcMemDC)   
  18.     {   
  19.         MessageBox(hWnd, L"StretchBlt has failed",L"Failed", MB_OK);   
  20.         goto done;   
  21.     }   
  22.   
  23.     // Get the client area for size calculation   
  24.     RECT rcClient;   
  25.     GetClientRect(hWnd, &rcClient);   
  26.     POINT pPos;   
  27.     pPos.x = xSrc;   
  28.     pPos.y = ySrc;   
  29.     ClientToScreen(hWnd, &pPos);//转换坐标   
  30.     //This is the best stretch mode   
  31.     //SetStretchBltMode(hdcWindow,HALFTONE);   
  32.   
  33.   
  34.        
  35.     // Create a compatible bitmap from the Window DC   
  36.     hbmScreen = CreateCompatibleBitmap(hdcScreen, nWidth, nHeight);   
  37.        
  38.     if(!hbmScreen)   
  39.     {   
  40.         MessageBox(hWnd, L"CreateCompatibleBitmap Failed",L"Failed", MB_OK);   
  41.         goto done;   
  42.     }   
  43.   
  44.     // Select the compatible bitmap into the compatible memory DC.   
  45.     SelectObject(hdcMemDC,hbmScreen);   
  46.        
  47.     // Bit block transfer into our compatible memory DC.   
  48.     if(!BitBlt(hdcMemDC,    
  49.                0,0,    
  50.                nWidth, nHeight,   
  51.                hdcScreen,    
  52.                pPos.x, pPos.y,   
  53.                SRCCOPY))   
  54.     {   
  55.         MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);   
  56.         goto done;   
  57.     }   
  58.   
  59.     // Get the BITMAP from the HBITMAP   
  60.     GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);   
  61.         
  62.     BITMAPFILEHEADER   bmfHeader;       
  63.     BITMAPINFOHEADER   bi;   
  64.         
  65.     bi.biSize = sizeof(BITMAPINFOHEADER);       
  66.     bi.biWidth = bmpScreen.bmWidth;       
  67.     bi.biHeight = bmpScreen.bmHeight;     
  68.     bi.biPlanes = 1;       
  69.     bi.biBitCount = 32;       
  70.     bi.biCompression = BI_RGB;       
  71.     bi.biSizeImage = 0;     
  72.     bi.biXPelsPerMeter = 0;       
  73.     bi.biYPelsPerMeter = 0;       
  74.     bi.biClrUsed = 0;       
  75.     bi.biClrImportant = 0;   
  76.   
  77.     DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;   
  78.     if(lpvbits)   
  79.     {   
  80.         delete[] lpvbits;   
  81.         lpvbits = NULL;   
  82.     }   
  83.     lpvbits = new DWORD[nWidth*nHeight];   
  84.   
  85.     // Gets the "bits" from the bitmap and copies them into a buffer    
  86.     // which is pointed to by lpbitmap.   
  87.     GetDIBits(hdcWindow, hbmScreen, 0,   
  88.         (UINT)bmpScreen.bmHeight,   
  89.         lpvbits,   
  90.         (BITMAPINFO *)&bi, DIB_RGB_COLORS);   
  91.   
  92.   
  93.     //Clean up   
  94. done:   
  95.     DeleteObject(hbmScreen);   
  96.     ReleaseDC(hWnd, hdcMemDC);   
  97.     ReleaseDC(NULL,hdcScreen);   
  98.     ReleaseDC(hWnd,hdcWindow);   
  99.   
  100.   
  101.     return true;   
  102. }  

这个是从MSDN上参考而来的。其实很简单,就是用ClientToScreen(hWnd, &pPos);把窗口中的坐标转换为对应的屏幕坐标,其他的和第一个是一样的。

附:

各图片位置,窗口大小等信息,这个找起来很麻烦。。。

m_nPicWidth = 498-1;  //左右两副图本身有偏移1个像素,去掉偏移的,只比较共有的部分
 m_nPicHeight = 448;
 m_nOffsetLeftPicX = 8;
 m_nOffsetLeftPicY = 193;
 m_nOffsetRightPicX = 516 + 1;   //左右两副图本身有偏移1个像素,去掉偏移的,只比较共有的部分
 m_nOffsetRightPicY = 193;

 

原创粉丝点击