I'm not good at winapi field.. so I want to ask your help.
Below is the code.
void DrawScreen(HDC hdc)
{
int x = 0;
int y = 0;
BITMAP bit;
HBITMAP backBitmap;
HBITMAP hOldBitmap;
HBITMAP hOldBitmap2;
HDC backMemDC, MemDc;
backBitmap = CreateCompatibleBitmap(hdc, rt.right, rt.bottom);
backMemDC = CreateCompatibleDC(hdc);
MemDc = CreateCompatibleDC(backMemDC);
//hOldBitmap2 = (HBITMAP)SelectObject(MemDc, backBmitmap2);
FillRect(backMemDC, &rt, (HBRUSH)GetStockObject(WHITE_BRUSH));
hOldBitmap = (HBITMAP)SelectObject(backMemDC, backBitmap);
for ( y = 0; y < 3; ++y)
{
for ( x = 0; x < 3; ++x)
{
switch (cChessBoard[y][x])
{
case '#':
iBit = WHITE_BOARD;
break;
case '#':
iBit = BLACK_BOARD;
break;
SelectObject(MemDc, hBit[iBit]);
}//end of switch
hOldBitmap2 = (HBITMAP)SelectObject(MemDc,hBit[iBit]);
BitBlt(backMemDC, x*BW, y*BH, BW, BH, MemDc, 0, 0, SRCCOPY);
}//end of for
}//end of for
hOldBitmap2 = (HBITMAP)SelectObject(MemDc, hBit[PLAYER]);
BitBlt(backMemDC, Player.m_iX*BW, Player.m_iY*BH, BW, BH, MemDc, 0, 0, SRCCOPY);
//InvalidateRect(hWndMain, NULL, FALSE);
BitBlt(hdc, 0, 0, rt.right, rt.bottom, backMemDC, 0, 0, SRCCOPY);
ReleaseDC(hWndMain,hdc);
DeleteObject(hOldBitmap);
DeleteDC(backMemDC);
DeleteDC(MemDc);
}
As you can see from the code above, I want to do double buffering.
But I still can not stop flickering... Someone help me please?
Moving to memory dc and moving things to hdc is not right?
Related
I am making a clipping software which clips the last 30 seconds of your screen. I am trying to write the results to an AVI file, but I am running into issues. Here is my code:
#include <Windows.h>
#include <Vfw.h>
#include <iostream>
#include <vector>
const int BUFFER_SIZE = 30 * 60; // 30 seconds at 60 fps
int main() {
HMONITOR hMonitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO info;
info.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(hMonitor, &info);
int width = info.rcMonitor.right - info.rcMonitor.left;
int height = info.rcMonitor.bottom - info.rcMonitor.top;
HDC hDC = GetDC(NULL);
HBITMAP hBitmap = CreateCompatibleBitmap(hDC, width, height);
// Create a device context for the bitmap
HDC hMemDC = CreateCompatibleDC(hDC);
SelectObject(hMemDC, hBitmap);
std::vector<HBITMAP> buffer(BUFFER_SIZE);
int index = 0;
while (true) {
BitBlt(hMemDC, 0, 0, width, height, hDC, 0, 0, SRCCOPY);
buffer[index] = hBitmap;
index = (index + 1) % BUFFER_SIZE;
if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) {
break;
}
}
// PROBLEM HERE:
PAVISTREAM pStream;
AVIFileInit();
AVIFileOpenW(&pStream, L"screen_recording.avi", OF_WRITE | OF_CREATE, NULL); // takes PAVIFILE as first parameter
AVIStreamSetFormat(pStream, 0, &hBitmap, sizeof(BITMAPINFOHEADER)); // takes PAVISTREAM as first parameter
// Write the stored frames to the AVISTREAM object
for (int i = 0; i < BUFFER_SIZE; i++) {
AVIStreamWrite(pStream, i, 1, &buffer[i], sizeof(BITMAPINFOHEADER), AVIIF_KEYFRAME, NULL, NULL);
}
AVIStreamClose(pStream);
AVIFileExit();
ReleaseDC(NULL, hDC);
DeleteDC(hMemDC);
DeleteObject(hBitmap);
return 0;
}
Am I doing this right? I am new to C++, so I am not sure if this is what I should be doing.
You can use the function AVIStreamOpenFromFile to open the avi file as stream, that will give the pointer to PAVISTREAM. So, not necessary to call the AVIFileOpen function.
I'm working on a C++ ATL COM thumbnail/preview/search project, but its bitmap displaying code behavior is monochrome during the Thumbnail process instead of the colored. The Preview process is colored as expected, using the same function.
I used the ATL Wizard to create the IThumbnailProvider and his friends. My small changes are: I replaced the color from black to pink in the document::OnDrawThumbnail and I wrote the document::OnDrawThumbnail into CPreviewCtrl::DoPaint. I've read the "new DC always monochrome" thing in the MS spec but I could not get colored DC even if a changed the original ATL code OnDrawThumbnail(GetDC(NULL), &rcBounds);. The CreateCompatibleDC(NULL) and CreateCompatibleDC(hDrawDC) were dead-end too.
document.cpp (changed)
// Almost the default sample code, but hDrawBrush is changed to pink
void document::OnDrawThumbnail(HDC hDrawDC, LPRECT lprcBounds)
{
HBRUSH hDrawBrush = CreateSolidBrush(RGB(255, 0, 255)); // pink
FillRect(hDrawDC, lprcBounds, hDrawBrush);
HFONT hStockFont = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
LOGFONT lf;
GetObject(hStockFont, sizeof(LOGFONT), &lf);
lf.lfHeight = 34;
HFONT hDrawFont = CreateFontIndirect(&lf);
HFONT hOldFont = (HFONT) SelectObject(hDrawDC, hDrawFont);
CString strText = _T("TODO: implement thumbnail drawing here");
DrawText(hDrawDC, strText, strText.GetLength(), lprcBounds, DT_CENTER | DT_WORDBREAK);
SelectObject(hDrawDC, hDrawFont);
SelectObject(hDrawDC, hOldFont);
DeleteObject(hDrawBrush);
DeleteObject(hDrawFont);
}
PreviewHandler.h (changed, it is called by the Preview)
// CPreviewCtrl implementation
class CPreviewCtrl : public CAtlPreviewCtrlImpl
{
protected:
virtual void DoPaint(HDC hdc)
{
// you can obtain a pointer to IDocument as follows
// CMyDoc* pDoc = (CMyDoc*)m_pDocument;
/*
CString strData = _T("Draw Rich Preview content here.");
TextOut(hdc, 10, 20, strData, strData.GetLength());
*/
RECT rc{};
rc.right = 290;
rc.bottom = 290;
dynamic_cast<document*>(m_pDocument)->OnDrawThumbnail(hdc, &rc);
}
};
atlhandlerimpl.h (unchanged, from VS SDK \atlmfc\include\ which is called be the thumbnail provider)
ATLPREFAST_SUPPRESS(6101)
_Success_(return != FALSE) BOOL GetThumbnail(
_In_ UINT cx,
_Out_ HBITMAP* phbmp,
_Out_ WTS_ALPHATYPE* /* pdwAlpha */)
{
HDC hdc = ::GetDC(NULL);
RECT rcBounds;
SetRect(&rcBounds, 0, 0, cx, cx);
HDC hDrawDC = CreateCompatibleDC(hdc);
if (hDrawDC == NULL)
{
ReleaseDC(NULL, hdc);
return FALSE;
}
HBITMAP hBmp = CreateCompatibleBitmap(hDrawDC, cx, cx);
if (hBmp == NULL)
{
ReleaseDC(NULL, hdc);
DeleteDC(hDrawDC);
return FALSE;
}
HBITMAP hOldBitmap = (HBITMAP) SelectObject(hDrawDC, hBmp);
// Here you need to draw the document's data
OnDrawThumbnail(hDrawDC, &rcBounds);
SelectObject(hDrawDC, hOldBitmap);
DeleteDC(hDrawDC);
ReleaseDC(NULL, hdc);
*phbmp = hBmp;
return TRUE;
}
ATLPREFAST_UNSUPPRESS()
Sample thumbnail in the File Explorer
Github helped me. It is definitely an ATL SDK bug.
BUG report on the VS developer community
Solution on the www.patthoyts.tk
And the github repo which helped me: abhimanyusirohi/ThumbFish
In the atlhandlerimpl.h provided GetThumbnail must be override:
BOOL document::GetThumbnail(
_In_ UINT cx,
_Out_ HBITMAP * phbmp,
_Out_ WTS_ALPHATYPE* /* pdwAlpha */)
{
BOOL br = FALSE;
HDC hdc = ::GetDC(NULL);
HDC hDrawDC = CreateCompatibleDC(hdc);
if (hDrawDC != NULL)
{
void* bits = 0;
RECT rcBounds;
SetRect(&rcBounds, 0, 0, cx, cx);
BITMAPINFO bi = { 0 };
bi.bmiHeader.biWidth = cx;
bi.bmiHeader.biHeight = cx;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biSizeImage = 0;
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biClrUsed = 0;
bi.bmiHeader.biClrImportant = 0;
HBITMAP hBmp = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &bits, NULL, 0);
if (hBmp != NULL)
{
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hDrawDC, hBmp);
OnDrawThumbnail(hDrawDC, &rcBounds);
SelectObject(hDrawDC, hOldBitmap);
*phbmp = hBmp;
br = TRUE;
}
DeleteDC(hDrawDC);
}
ReleaseDC(NULL, hdc);
return br;
}
basically what i'm trying to achieve is a continuous flow of taking snapshots of specific part of a window. Up to now I've managed to take the screenshot and parse it pixelwise. However, all screenshots are black. Please help this newbie. No errors returned, FindWindow works and returns the correct window(to appwnd). I've been debugging this for the past 3 days and i'm out of ideas. Also i need to mention i am releasing the memory after using everything, to not clamp up my ram.I'm running Windows 10 last patch.
snippet of Code:
BOOL myfunction(appwnd){
RECT rcWindow;
GetWindowRect(appwnd, &rcWindow);
BITMAP bm;
HBITMAP hbmap;
HBITMAP hbmapold;
BITMAPINFO bmi;
HDC hdcShot;
//a while starts here(not the issue)
RECT rc;//get window rectangle
GetWindowRect(appwnd, &rc);
//creating bitmaps
HDC hdc = GetDC(0);
hdcShot = CreateCompatibleDC(hdc);
hbmap = CreateCompatibleBitmap(hdc, rc.right - rc.left, rc.bottom - rc.top);
//
if (!BitBlt(hdcShot, 0, 0, rc.right - rc.left, rc.bottom - rc.top, GetDC(0), rc.left, rc.right, SRCCOPY | CAPTUREBLT))
{
printf("bitblt failed");
system("pause");
}
SelectObject(hdcShot, hbmap);
if (!GetObject(hbmap, sizeof(BITMAP), (LPSTR)&bm))
{
printf("error at getobject");
system("pause");
return false;
}
int bitsperpixel = bm.bmBitsPixel;
if (bitsperpixel != 32 || bm.bmPlanes != 1)
{
printf("error at bitsperpixel/bm planes");
system("pause");
return false;
}
SetupBitmapInfo(bmi, bm.bmWidth, bm.bmHeight, bitsperpixel);
HDC hdcShotNew = CreateCompatibleDC(hdcShot);
HBITMAP hbmapNew = CreateCompatibleBitmap(hdcShot, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top);
HBITMAP OldBmp = (HBITMAP)SelectObject(hdcShotNew, hbmapNew);
int width = rcWindow.right - rcWindow.left;
int height = rcWindow.bottom - rcWindow.top;
printf("\nwidth:%i", width);
printf("\nheight:%i", height);
//copy the screen using bitblt
BitBlt(hdcShotNew, 0, 0, width, height , hdcShot,0,0,SRCCOPY|CAPTUREBLT);
pPixels = new RGBQUAD[bm.bmWidth * bm.bmHeight];
if (!pPixels)
{
printf("ppixels failed");
return false;
}
SelectObject(hdcShotNew, OldBmp);
if (GetDIBits(hdcShotNew, hbmapNew, 0, bm.bmHeight, pPixels, &bmi, DIB_RGB_COLORS)==0)
{
ReleaseDC(appwnd, hdcShot);
printf("getdibits failed");
delete[] pPixels;
return false;
}
}
Snippet of printing pixels :
printf("\n%i,%i,%i", (int)pPixels[p].rgbRed, (int)pPixels[p].rgbGreen, (int)pPixels[p].rgbBlue);
all pixels are 0, always.
Snippet of SetupBitmapInfo:
void SetupBitmapInfo(BITMAPINFO& bmi, int bWidth, int bHeight, int bitsperpixel)
{
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = bWidth;
bmi.bmiHeader.biHeight = bHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = bitsperpixel;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0;
}
I want the code below to take a screenshot of a specified window only, becuse this way BitBlt is faster.
The code below takes a screenshot of a window, specified by the name of window, loads the pixel data in a buffer and then re-draws the picture on your screen just to prove the copying worked.
I'd like to take screenshots of browser windows like Google Chrome, but it doesn't seem to work. It draws a black rectangle on my screen with the correct dimensions of the window.
Seems to be working all the time with the window of Minecraft.
Also worked with the Tor Browser.
I noticed that after querying window info, minecraft's window had no child-windows, but when it didn't work with the others, all of them had multiple child-windows.
#include<iostream>
#include<Windows.h>
#include<vector>
using namespace std;
int main() {
HWND wnd = FindWindow(NULL, "Minecraft 1.15.1");//Name of window to be screenshoted
if (!wnd) {
std::cout << "e1\n";
std::cin.get();
return 0;
}
WINDOWINFO wi = { 0 };
wi.cbSize = sizeof(WINDOWINFO);
GetWindowInfo(wnd, &wi);
RECT rect;
GetWindowRect(wnd, &rect);
int width = wi.rcClient.right - wi.rcClient.left;
int height = wi.rcClient.bottom - wi.rcClient.top;
cout << width << height;
/////Fetch window info^^^^^^^^
BYTE* ScreenData = new BYTE[4 * width*height];
// copy screen to bitmap
HDC hScreen = GetWindowDC(wnd);
HDC hDC = CreateCompatibleDC(hScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, width, height);
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BitBlt(hDC, 0, 0, width, height, hScreen, 0, 0, SRCCOPY);
SelectObject(hDC, old_obj);
BITMAPINFO bmi = { 0 };
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
std::cout << GetDIBits(hDC, hBitmap, 0, 0, NULL, &bmi, DIB_RGB_COLORS)<<endl;
//std::cout << bmi.bmiHeader.biHeight<< " "<< bmi.bmiHeader.biWidth<<endl;
BYTE*buffer = new BYTE[4* bmi.bmiHeader.biHeight*bmi.bmiHeader.biWidth];
bmi.bmiHeader.biHeight *= -1;
std::cout<<GetDIBits(hDC, hBitmap, 0, bmi.bmiHeader.biHeight, buffer, &bmi, DIB_RGB_COLORS)<<endl;//DIB_PAL_COLORS
/////Load window pixels into a buffer^^^^^^^^
POINT p;
int r = 0;
int g = 0;
int b = 0;
int x = 0;
int y = 0;
HDC sc = GetDC(NULL);
COLORREF color;
while (true) {
if ((y*-1) == bmi.bmiHeader.biHeight+1 && x == bmi.bmiHeader.biWidth-1) { break; }
r = (int)buffer[4 * ((y*bmi.bmiHeader.biWidth) + x)+2];
g = (int)buffer[4 * ((y*bmi.bmiHeader.biWidth) + x)+1];
b = (int)buffer[4 * ((y*bmi.bmiHeader.biWidth) + x) ];
color = RGB(r, g, b);
SetPixel(sc, x, y, color);
x++;
if (x == bmi.bmiHeader.biWidth) {
y++;
x = 0;
}
}
/////Prove that the copying was successful and buffer is full^^^^^^^^
Sleep(5000);
std::cout << "fin\n";
std::cin.get();
return 0;
}
I think the problem is the order you're doing things.
Before you put the bitmap on the clipboard, you should select it out of your memory device context.
Once you put the bitmaps on the clipboard, you no longer own it, so you shouldn't try to delete it.
The best way to do that is probably to move your // clean up section before the // save bitmap to clipboard section and to eliminate your DelectObject(hBitmap) statement. So the tail end of your code should probably be:
BitBlt(hDC, 0, 0, width, height, hScreen, 0, 0, SRCCOPY);
// clean up
SelectObject(hDC, old_obj); // selects hBitmap out of hDC
DeleteDC(hDC);
ReleaseDC(NULL, hScreen);
// save bitmap to clipboard
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap); // clipboard now owns the bitmap
CloseClipboard();
If you still have a problem after those changes, I would check the return value of the SetClipboardData call. If that's failing, GetLastError may give a clue.
I'm trying to make an auto-cliker for an windows app. It works well, but it's incredibly slow!
I'm currently using the method "getPixel" which reloads an array everytime it's called.
Here is my current code:
hdc = GetDC(HWND_DESKTOP);
bx = GetSystemMetrics(SM_CXSCREEN);
by = GetSystemMetrics(SM_CYSCREEN);
start_bx = (bx/2) - (MAX_WIDTH/2);
start_by = (by/2) - (MAX_HEIGHT/2);
end_bx = (bx/2) + (MAX_WIDTH/2);
end_by = (by/2) + (MAX_HEIGHT/2);
for(y=start_by; y<end_by; y+=10)
{
for(x=start_bx; x<end_bx; x+=10)
{
pixel = GetPixel(*hdc, x, y);
if(pixel==RGB(255, 0, 0))
{
SetCursorPos(x,y);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Sleep(50);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Sleep(25);
}
}
}
So basically, it just scan a range of pixel in the screen and starts a mouse event if it detects a red button.
I know there are other ways to get the pixel color, such as bitblt. But I've made some researches, and I don't understand how I'm supposed to do, in order to scan a color array. I need something which scans screen very fast in order to catch the button.
Could you please help me?
Thanks.
I found a perfect way which is clearly faster than the GetPixel one:
HDC hdc, hdcTemp;
RECT rect;
BYTE* bitPointer;
int x, y;
int red, green, blue, alpha;
while(true)
{
hdc = GetDC(HWND_DESKTOP);
GetWindowRect(hWND_Desktop, &rect);
int MAX_WIDTH = rect.right;
int MAX_HEIGHT = rect.bottom;
hdcTemp = CreateCompatibleDC(hdc);
BITMAPINFO bitmap;
bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
bitmap.bmiHeader.biWidth = MAX_WIDTH;
bitmap.bmiHeader.biHeight = MAX_HEIGHT;
bitmap.bmiHeader.biPlanes = 1;
bitmap.bmiHeader.biBitCount = 32;
bitmap.bmiHeader.biCompression = BI_RGB;
bitmap.bmiHeader.biSizeImage = MAX_WIDTH * 4 * MAX_HEIGHT;
bitmap.bmiHeader.biClrUsed = 0;
bitmap.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap2 = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
SelectObject(hdcTemp, hBitmap2);
BitBlt(hdcTemp, 0, 0, MAX_WIDTH, MAX_HEIGHT, hdc, 0, 0, SRCCOPY);
for (int i=0; i<(MAX_WIDTH * 4 * MAX_HEIGHT); i+=4)
{
red = (int)bitPointer[i];
green = (int)bitPointer[i+1];
blue = (int)bitPointer[i+2];
alpha = (int)bitPointer[i+3];
x = i / (4 * MAX_HEIGHT);
y = i / (4 * MAX_WIDTH);
if (red == 255 && green == 0 && blue == 0)
{
SetCursorPos(x,y);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Sleep(50);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Sleep(25);
}
}
}
I hope this could help someone else.
The simple answer is that if this is the method you insist on using then there isn't much to optimize. As others have pointed out in comments, you should probably use a different method for locating the area to click. Have a look at using FindWindow, for example.
If you don't want to change your method, then at least sleep your thread for a bit after each complete screen scan.