I'm writing a remote controlling program with GUI.
Here's a part of the server code:
HDC desktopHdc = GetDC(NULL);
int bm_x = GetDeviceCaps(desktopHdc, HORZRES);
int bm_y = GetDeviceCaps(desktopHdc, VERTRES);
DWORD bmSize = ((bm_x * 32 + 31) / 32) * bm_y * 4;
HDC memHdc = CreateCompatibleDC(desktopHdc);
HBITMAP bitmap = CreateCompatibleBitmap(desktopHdc, bm_x, bm_y);
while(TRUE)
{
SelectObject(memHdc, (HGDIOBJ)bitmap);
HANDLE hDIB = GlobalAlloc(GHND, bmSize);
char* bmbits = (char*)GlobalLock(hDIB);
BitBlt(memHdc, 0, 0, bm_x, bm_y, desktopHdc, 0, 0, SRCCOPY);
GetBitmapBits(bitmap, bmSize, bmbits);
int bytes = send(secondarySocket, bmbits, bmSize, 0);
if(bytes == 0 || bytes == -1)
{
MessageBox(NULL, "disconnected", "", 0);
break;
}
GlobalUnlock(hDIB);
GlobalFree(hDIB);
Sleep(1000);
}
And here's some client code:
HDC windowHdc = GetDC(hwnd);
HDC memHdc = CreateCompatibleDC(windowHdc);
// bm_x and bm_y are sent by the server and received by the client
DWORD bmSize = ((bm_x * 32 + 31) / 32) * bm_y * 4;
while(TRUE)
{
HANDLE hDIB = GlobalAlloc(GHND, bmSize);
char* buf = (char*)GlobalLock(hDIB);
int bytes = recv(mainSocket, buf, bmSize, 0);
if(bytes == 0 || bytes == -1)
{
MessageBox(NULL, "disconnected", "", 0);
break;
}
else
{
buf[bytes] = '\0';
HBITMAP bmp = CreateCompatibleBitmap(memHdc, bm_x, bm_y);
SetBitmapBits(bmp, bmSize, buf);
SelectObject(memHdc, bmp);
BitBlt(windowHdc, 0, 0, bm_x, bm_y, memHdc, 0, 0, SRCCOPY); // should copy received bitmap to the window DC
}
GlobalUnlock(hDIB);
GlobalFree(hDIB);
}
I receive whole buffer in the client correctly. I make a bitmap from it. And then I just can't copy it to the window DC - nothing appears. How can I do that?
EDIT
Here's my new code:
DWORD thServerSend(LPVOID param)
{
HDC memHdc = CreateCompatibleDC(desktopHdc);
HBITMAP bitmap;
while(TRUE)
{
BITMAPINFO bmi;
ZeroMemory(&bmi,sizeof(BITMAPINFO));
bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth=bm_x;
bmi.bmiHeader.biHeight=bm_y;
bmi.bmiHeader.biPlanes=1;
bmi.bmiHeader.biBitCount=32;
char* bmbits;
bitmap = CreateDIBSection(memHdc, &bmi, DIB_RGB_COLORS, (void**)&bmbits, NULL, 0);
HBITMAP restore = (HBITMAP)SelectObject(memHdc, (HGDIOBJ)bitmap);
BitBlt(memHdc, 0, 0, bm_x, bm_y, desktopHdc, 0, 0, SRCCOPY);
int bytes = send(secondarySocket, bmbits, bmSize, 0);
if(bytes == 0 || bytes == -1)
{
MessageBox(NULL, "disconnected", "", 0);
break;
}
std::cout << "bmsize: " << bmSize << " bytes: " << bytes << " bmbits: " << bmbits << std::endl; // for debugging
SelectObject(memHdc, restore);
DeleteObject(bitmap);
Sleep(1000);
}
DeleteDC(memHdc);
DeleteObject(bitmap);
shutdown(mainSocket, SD_BOTH);
shutdown(secondarySocket, SD_BOTH);
return 0;
}
DWORD thClient(LPVOID param) // window handle is passed through "param" parameter
{
HDC windowHdc = GetDC((HWND)param);
HDC memHdc = CreateCompatibleDC(windowHdc);
HBITMAP restore;
while(TRUE)
{
BITMAPINFO bmi;
ZeroMemory(&bmi,sizeof(BITMAPINFO));
bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth=bm_x;
bmi.bmiHeader.biHeight=bm_y;
bmi.bmiHeader.biPlanes=1;
bmi.bmiHeader.biBitCount=32;
char* buffer;
HBITMAP bitmap = CreateDIBSection(windowHdc, &bmi, DIB_RGB_COLORS, (void**)&buffer, NULL, 0);
int bytes = recv(mainSocket, buffer, bmSize, 0);
if(bytes == 0 || bytes == -1)
{
MessageBox(NULL, "Disconnected.", "", 0);
break;
}
else
{
restore = (HBITMAP)SelectObject(memHdc,bitmap);
BitBlt(windowHdc, 0, 0, bm_x, bm_y, memHdc, 0, 0, SRCCOPY);
std::cout << "bmsize: " << bmSize << " bytes: " << bytes << " bmbits: " << buffer << std::endl; // for debugging
SelectObject(memHdc, restore);
}
}
DeleteDC(memHdc);
return 0;
}
EDIT 2
Problem solved:
That is, how I called the threads:
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thClient, (PVOID)&hwnd, 0, NULL);
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thServerSend, (PVOID)hwnd, 0, NULL);
As you can see, I used reference to the window handle in the argument of the thread calling function. This secretly caused stupid errors. Nobody could solve it without full code.
Don't create a compatible bitmap.
Create a DIB section: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183494%28v=vs.85%29.aspx
BITMAPINFO bi;
ZeroMemory(&bi,sizeof(BITMAPINFO));
bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth=Width;
bi.bmiHeader.biHeight=Height;
bi.bmiHeader.biPlanes=1;
bi.bmiHeader.biBitCount=32;
char* buffer;
HBITMAP bitmap=CreateDIBSection(dc,&bi,DIB_RGB_COLORS,&buffer,NULL,0);
// copy pixels to buffer
SelectObject(dc,bitmap);
BitBlt(....
Related
I am receiving those screenshots in WM_PAINT, and then save it to memory. The screenshots have around 8-10kB size. And I am trying to display those screenshots in a 15 FPS, but the problem is that its displaying sometimes glitched screenshots like this:
[the gray square with dots inside it, that should not be there, its covering the half of the screenshot :c ]
Image displayed like this, is like every second or third screenshot. I have no idea how to fix this im stuck a couple days at this.
Here is the server code (window procedure) where i am trying to display these screenshots
LRESULT CALLBACK ClientWndProc(HWND Chwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
std::vector<char> buffer(50000);
switch (uMsg)
{
case WM_CREATE:
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
SetTimer(Chwnd, SCREEN_TIMER, 1000/15, NULL);
break;
}
case WM_TIMER:
{
if (wParam == SCREEN_TIMER)
{
SOCKET selectedSocket = clientSockets[itemIndex];
auto it = std::find(clientSockets.begin(), clientSockets.end(), selectedSocket);
send(selectedSocket, "capscr", strlen("capscr"), 0);
InvalidateRect(Chwnd, NULL, TRUE);
}
break;
}
case WM_PAINT:
{
SOCKET selectedSocket = clientSockets[itemIndex];
auto it = std::find(clientSockets.begin(), clientSockets.end(), selectedSocket);
if (it != clientSockets.end())
{
int bytesReceived = 0;
int totalBytesReceived = 0;
int expectedBytes = buffer.size();
Sleep(1000/15);
do
{
bytesReceived = recv(selectedSocket, buffer.data() + totalBytesReceived, buffer.size() - totalBytesReceived, 0);
totalBytesReceived += bytesReceived;
} while (totalBytesReceived < expectedBytes && bytesReceived > 0);
//MessageBoxA(NULL, buffer.data(), "s", MB_OK);
HGLOBAL hGlobal = GlobalAlloc(GHND, expectedBytes);
void* pData = GlobalLock(hGlobal);
memcpy(pData, buffer.data(), buffer.size());
GlobalUnlock(hGlobal);
IStream* pStream = NULL;
CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
Gdiplus::Bitmap bitmap(pStream);
Gdiplus::Status status = bitmap.GetLastStatus();
PAINTSTRUCT ps;
HDC hdc = BeginPaint(Chwnd, &ps);
int imgWidth = bitmap.GetWidth();
int imgHeight = bitmap.GetHeight();
Gdiplus::Graphics graphics(hdc);
RECT clientRect;
GetClientRect(Chwnd, &clientRect);
graphics.DrawImage(&bitmap, 0, 0, imgWidth, imgHeight);
EndPaint(Chwnd, &ps);
GlobalFree(hGlobal);
}
break;
}
case WM_ERASEBKGND:
return TRUE;
case WM_CLOSE:
{
DestroyWindow(hwndClient);
break;
}
case WM_DESTROY:
DestroyWindow(hwndClient);
return 0;
default:
return DefWindowProc(Chwnd, uMsg, wParam, lParam);
}
return 0;
}
client code sending screenshots:
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return 0;
}
void shutdownGdiPlus()
{
Gdiplus::GdiplusShutdown(gdiPlusToken);
gdiPlusToken = NULL;
}
bool initGdiPlusIfNeeded()
{
// If already initialized then return true
if (gdiPlusToken != NULL)
return true;
static Gdiplus::GdiplusStartupInput gdiPlusStartupInput;
return (success = GdiplusStartup(&gdiPlusToken, &gdiPlusStartupInput, NULL)) == Gdiplus::Status::Ok;
}
std::pair<ULONGLONG, char*> getScreenShotAsByteArray()
{
if (!initGdiPlusIfNeeded())
return {};
IStream* iStream;
HRESULT res = CreateStreamOnHGlobal(NULL, true, &iStream);
const HDC srcDC = ::GetDC(NULL);
const int screenHeight = GetSystemMetrics(SM_CYSCREEN);
const int screenWidth = GetSystemMetrics(SM_CXSCREEN);
const HDC memDC = CreateCompatibleDC(srcDC);
const HBITMAP membit = CreateCompatibleBitmap(srcDC, screenWidth, screenHeight);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(memDC, membit);
BitBlt(memDC, 0, 0, screenWidth, screenHeight, srcDC, 0, 0, SRCCOPY);
// Create a bitmap to store the previous screenshot
HBITMAP prevBitmap = CreateCompatibleBitmap(srcDC, screenWidth, screenHeight);
HDC prevDC = CreateCompatibleDC(srcDC);
SelectObject(prevDC, prevBitmap);
// Get the size of the bitmaps in bytes
BITMAP bmp;
GetObject(prevBitmap, sizeof(BITMAP), &bmp);
int prevBmpSize = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
GetObject(membit, sizeof(BITMAP), &bmp);
int currBmpSize = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
// Allocate memory for the bitmap data
char* prevBmpData = new char[prevBmpSize];
char* currBmpData = new char[currBmpSize];
// Get the raw pixel data of the bitmaps
GetBitmapBits(prevBitmap, prevBmpSize, prevBmpData);
GetBitmapBits(membit, currBmpSize, currBmpData);
// Compare the bitmap data
bool isDifferent = memcmp(prevBmpData, currBmpData, currBmpSize) != 0;
// Free the allocated memory
delete[] prevBmpData;
delete[] currBmpData;
// Check if the current screenshot is different from the previous screenshot
if (isDifferent)
{
// Screenshot is different, take a new screenshot
int newScreenWidth = 700;
int newScreenHeight = 500;
Gdiplus::Bitmap fullScreenBitmap(membit, NULL);
Gdiplus::Bitmap newBitmap(newScreenWidth, newScreenHeight);
Gdiplus::Graphics graphics(&newBitmap);
graphics.DrawImage(&fullScreenBitmap, 0, 0, newScreenWidth, newScreenHeight);
CLSID clsid;
GetEncoderClsid(L"image/jpeg", &clsid);
ULONG quality = 0;
EncoderParameters encoderParams;
encoderParams.Count = 1;
encoderParams.Parameter[0].Guid = EncoderQuality;
encoderParams.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParams.Parameter[0].NumberOfValues = 1;
encoderParams.Parameter[0].Value = &quality;
newBitmap.Save(iStream, &clsid, &encoderParams);
ULARGE_INTEGER pos{ 0, 0 };
const LARGE_INTEGER pos2{ 0, 0 };
iStream->Seek(pos2, STREAM_SEEK_SET, &pos);
ULONG bytesWritten = 0;
STATSTG statstg;
iStream->Stat(&statstg, STATFLAG_DEFAULT);
const ULONGLONG bufferLen = statstg.cbSize.QuadPart;
char* imageBuffer = new char[bufferLen];
iStream->Read(imageBuffer, bufferLen, &bytesWritten);
iStream->Release();
DeleteObject(memDC);
DeleteObject(membit);
::ReleaseDC(NULL, srcDC);
std::pair<ULONGLONG, char*> result(bufferLen, imageBuffer);
return result;
}
else
{
return {};
}
}
void sendScreens() {
if (clientsocket == INVALID_SOCKET) {
// handle error, the socket is not valid or not connected
return;
}
std::pair<ULONGLONG, char*> image = getScreenShotAsByteArray();
ULONGLONG bufferLen = image.first;
char* imageBuffer = image.second;
int bytesSent = send(clientsocket, imageBuffer, bufferLen, 0);
if(bytesSent < 0)
{
return;
}
std::cout << "Sent Bytes: " << bytesSent << "\n";
// wait for the desired interval before sending the next screenshot
std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::time_point end = start + std::chrono::milliseconds(FPS);
std::this_thread::sleep_until(end);
}
I am using this code to capture screen and cursor.
// ConsoleApplication1.cpp : This file contains the 'main' function. Program execution begins and ends there.
#include <Windows.h>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace std;
using namespace cv;
HWND hwndDesktop = NULL;
bool flg;
CURSORINFO cursor;
ICONINFOEXW info;
BITMAP bmpCursor;
HDC hwindowDC, hwindowCompatibleDC;
HBITMAP hbwindow;
int height, width, srcheight, srcwidth;
Mat src;
BITMAPINFOHEADER bi;
RECT windowsize;
Mat hwnd2mat(HWND hwnd)
{
src.create(height, width, CV_8UC4);
int val = 0;
// use the previously created device context with the bitmap
HGDIOBJ hPrevBitmap = SelectObject(hwindowCompatibleDC, hbwindow);
// copy from the window device context to the bitmap device context
val = StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, 0, 0, srcwidth, srcheight, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
if (val <= 0) {
DWORD error_ = GetLastError();
cout << "error " << error_ << endl;
}
cursor = { sizeof(cursor) };
::GetCursorInfo(&cursor);
if (cursor.flags == CURSOR_SHOWING and flg) {
info = { sizeof(info) };
::GetIconInfoExW(cursor.hCursor, &info);
const int x = cursor.ptScreenPos.x - windowsize.left - info.xHotspot;
const int y = cursor.ptScreenPos.y - windowsize.top - info.yHotspot;
bmpCursor = { 0 };
val = ::GetObject(info.hbmColor, sizeof(bmpCursor), &bmpCursor);
val = ::DrawIconEx(hwindowCompatibleDC, x, y, cursor.hCursor, bmpCursor.bmWidth, bmpCursor.bmHeight,
0, NULL, DI_NORMAL);
if (val <= 0) {
DWORD error_ = GetLastError();
cout << "error " << error_ << endl;
flg = false;
}
}
val = GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, src.data, (BITMAPINFO *)&bi, DIB_RGB_COLORS); //copy from hwindowCompatibleDC to hbwindow
if (val <= 0) {
DWORD error_ = GetLastError();
cout << "error " << error_ << endl;
GdiFlush();
flg = false;
}
SelectObject(hwindowCompatibleDC, hPrevBitmap);
return src;
}
int main(int argc, char **argv)
{
flg = true;
hwndDesktop = GetDesktopWindow();
namedWindow("output", WINDOW_NORMAL);
int key = 0;
hwindowDC = GetDC(hwndDesktop);
if (hwindowDC == NULL) {
DWORD error_ = GetLastError();
cout << "error " << error_ << endl;
}
hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
if (hwindowCompatibleDC == NULL) {
DWORD error_ = GetLastError();
cout << "error " << error_ << endl;
}
SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);
GetClientRect(hwndDesktop, &windowsize);
srcheight = windowsize.bottom;
srcwidth = windowsize.right;
height = windowsize.bottom / 1; //change this to whatever size you want to resize to
width = windowsize.right / 1;
hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
if (hbwindow == NULL) {
DWORD error_ = GetLastError();
cout << "error " << error_ << endl;
}
bi.biSize = sizeof(BITMAPINFOHEADER); //http://msdn.microsoft.com/en-us/library/windows/window/dd183402%28v=vs.85%29.aspx
bi.biWidth = width;
bi.biHeight = -height; //this is the line that makes it draw upside down or not
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;
while (key != 27)
{
Mat src = hwnd2mat(hwndDesktop);
// you can do some image processing here
imshow("output", src);
key = waitKey(1); // you can change wait time
}
}
The Screen Capture works perfectly , but always fails after screen capture of just more than 8 mins and 30 seconds.
After 8 min and 30 seconds GetDIBits starts to return 0 .
I am running this on a 64bit machine with windows 10 professional ediiton.
This Issue seems to not apear if i remove the cursor capturing code .
DeleteObject(info.hbmColor);
DeleteObject(info.hbmMask);
Adding these lines did the trick.
Thanks to Raymond Chen and IInspectable
I am extending an old console application. Trying to add a window( for an animation ). Are able to display first image, but unable to update the window with later images. Have created a minimum application to demonstrate the problem. In this app I have a red window that should change at each update of the window( it is not ). What am I doing wrong ?
#include "pch.h"
#include <iostream>
#include <Windows.h>
using namespace std;
HWND hWindow;
LRESULT __stdcall MyWindowProcedure(HWND hWnd, unsigned int msg, WPARAM wp, LPARAM lp)
{
PAINTSTRUCT ps;
HDC hdc;
HBITMAP hBitmap;
VOID * pvBits;
RGBQUAD *bmiColors;
static int j = 128;
HBITMAP hOldBmp;
BITMAPINFO MyBitmap;
BOOL qRetBlit;
switch (msg)
{
case WM_DESTROY:
std::cout << "\ndestroying window\n";
PostQuitMessage(0);
return 0L;
case WM_LBUTTONDOWN:
std::cout << "\nmouse left button down at (" << LOWORD(lp)
<< ',' << HIWORD(lp) << ")\n";
// fall thru
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
#if 1
// Create a device context that is compatible with the window
HDC hLocalDC;
hLocalDC = ::CreateCompatibleDC(hdc);
// Verify that the device context was created
if (hLocalDC == NULL) {
printf("CreateCompatibleDC Failed\n");
// return false;
break;
}
MyBitmap.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
MyBitmap.bmiHeader.biWidth = 256;
MyBitmap.bmiHeader.biHeight = -256;
MyBitmap.bmiHeader.biPlanes = 1;
MyBitmap.bmiHeader.biBitCount = 32;
MyBitmap.bmiHeader.biCompression = BI_RGB;
MyBitmap.bmiHeader.biSizeImage = 256 * 256 * 4;
MyBitmap.bmiHeader.biYPelsPerMeter = 1000;
MyBitmap.bmiHeader.biXPelsPerMeter = 1000;
MyBitmap.bmiHeader.biClrUsed = 0;
MyBitmap.bmiHeader.biClrImportant = 0;
hBitmap = CreateDIBSection(NULL, &MyBitmap, DIB_RGB_COLORS, &pvBits, NULL, NULL);
bmiColors = (RGBQUAD*)pvBits;
// note: BGR
for (int i = 0; i < 256 * 256; i++)
{
bmiColors[i].rgbBlue = 0;
bmiColors[i].rgbGreen = 0;
bmiColors[i].rgbRed = j % 256;
bmiColors[i].rgbReserved = 255;
}
j += 10;
bmiColors[1000].rgbRed;
// Select the bitmap into the device context
hOldBmp = (HBITMAP)::SelectObject(hLocalDC, hBitmap);
if (hOldBmp == NULL) {
printf("SelectObject Failed");
// return false;
break;
}
// Blit the dc which holds the bitmap onto the window's dc
qRetBlit = ::BitBlt(hdc, 0, 0, 256, 256, hLocalDC, 0, 0, SRCCOPY);
if (!qRetBlit) {
printf("Blit Failed");
// return false;
break;
}
// Unitialize and deallocate resources
SelectObject(hLocalDC, hOldBmp);
DeleteDC(hLocalDC);
DeleteObject(hBitmap);
#endif
EndPaint(hWnd, &ps);
break;
default:
std::cout << '.';
break;
}
return DefWindowProc(hWnd, msg, wp, lp);
}
int main()
{
WNDCLASSEX wndclass = { sizeof(WNDCLASSEX), CS_DBLCLKS | CS_HREDRAW, MyWindowProcedure,
0, 0, GetModuleHandle(0), LoadIcon(0,IDI_APPLICATION),
LoadCursor(0,IDC_ARROW), HBRUSH(COLOR_WINDOW + 1),
0, L"myclass", LoadIcon(0,IDI_APPLICATION) };
if (RegisterClassEx(&wndclass))
{
hWindow = CreateWindowEx(0, L"myclass", L"myclass", WS_OVERLAPPEDWINDOW, 1200, 600, 256 + 15, 256 + 38, 0, 0, GetModuleHandle(0), 0);
ShowWindow(hWindow, SW_SHOWDEFAULT);
UpdateWindow(hWindow);
MSG msg;
byte bRet;
while ((bRet = GetMessage(&msg, hWindow, 0, 0)) != 0)
{
if (bRet == -1)
{
return 1;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
As pointed out. You do not want to call WM_PAINT your self. Let the system handle it. So in WM_LBUTTONDOWN do:
case WM_LBUTTONDOWN:
std::cout << "\nmouse left button down at (" << LOWORD(lp)
<< ',' << HIWORD(lp) << ")\n";
InvalidateRect(hWnd, nullptr, FALSE);
return 0;
Do not fall through. Wnen you call RedrawWindow the window is invalidated and then needs to be repainted. I just tested this, the window lightens up on each click.
UPDATE:
Thanks to Remy Lebeau
Difference between InvalidateRect and RedrawWindow
And yes, it is better to let windows use its own schedule for this kind of stuff.
It writes to my BITMAP.bmp file but when I try to view it in Windows Photo Viewer It says "Windows Photo Viewer can't open this picture because the file appears to be damaged, corrupted, or is too large." I know I probably should have put the functions into header files but I haven't ever really delved deeply enough into a project to really make header files so I forgot how. If anyone knows the size limit for pictures in Windows Photo Viewer I would be very thankful. And I got some of these functions from MSDN(unashamed). The error handling could be better but I don't have errorhandler.h. I am not completely sure how all of the functions work because as I said I used some code from MSDN.
I would be greatly appreciative of anyone that can help me. :)
#include <errorrep.h>
#include <windows.h>
#include <iostream>
using namespace std;
namespace Globals{
HBITMAP hBitmap;
HDC hScreen;
}
using namespace Globals;
void GetScreenShot(void)
{
int x1, y1, x2, y2, w, h;
LPSIZE lpSize;
LPVOID lpvBits;
// get screen dimensions
x1 = GetSystemMetrics(SM_XVIRTUALSCREEN);
x2 = GetSystemMetrics(SM_CXVIRTUALSCREEN);
y1 = GetSystemMetrics(SM_YVIRTUALSCREEN);
y2 = GetSystemMetrics(SM_CYVIRTUALSCREEN);
w = x2-x1;
h = y2-y1;
// copy screen to bitmap
hScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hScreen);
hBitmap = CreateCompatibleBitmap(hScreen, w, h);
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BOOL bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY);
GetBitmapDimensionEx(hBitmap, lpSize);
GetBitmapBits(hBitmap, (LONG)lpSize, lpvBits);
// save bitmap to clipboard
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap);
CloseClipboard();
// clean up
SelectObject(hDC, old_obj);
/*DeleteDC(hDC);
ReleaseDC(NULL, hScreen);
DeleteObject(hBitmap);*/
}
PBITMAPINFO CreateBitmapInfoStruct(/*HWND hwnd, */HBITMAP hBmp)
{
BITMAP bmp;
PBITMAPINFO pbmi;
WORD cClrBits;
// Retrieve the bitmap color format, width, and height.
if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
//errhandler("GetObject", hwnd);
cout << "Error: CreateBitmapInfoStruct" << endl;
// Convert the color format to a count of bits.
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
if (cClrBits == 1)
cClrBits = 1;
else if (cClrBits <= 4)
cClrBits = 4;
else if (cClrBits <= 8)
cClrBits = 8;
else if (cClrBits <= 16)
cClrBits = 16;
else if (cClrBits <= 24)
cClrBits = 24;
else cClrBits = 32;
// Allocate memory for the BITMAPINFO structure. (This structure
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD
// data structures.)
if (cClrBits < 24)
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * (1<< cClrBits));
// There is no RGBQUAD array for these formats: 24-bit-per-pixel or 32-bit-per-pixel
else
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER));
// Initialize the fields in the BITMAPINFO structure.
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
if (cClrBits < 24)
pbmi->bmiHeader.biClrUsed = (1<<cClrBits);
// If the bitmap is not compressed, set the BI_RGB flag.
pbmi->bmiHeader.biCompression = BI_RGB;
// Compute the number of bytes in the array of color
// indices and store the result in biSizeImage.
// The width must be DWORD aligned unless the bitmap is RLE
// compressed.
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
* pbmi->bmiHeader.biHeight;
// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;
return pbmi;
}
void CreateBMPFile(/*HWND hwnd, */LPTSTR pszFile, PBITMAPINFO pbi,
HBITMAP hBMP, HDC hDC)
{
HANDLE hf; // file handle
BITMAPFILEHEADER hdr; // bitmap file-header
PBITMAPINFOHEADER pbih; // bitmap info-header
LPBYTE lpBits; // memory pointer
DWORD dwTotal; // total count of bytes
DWORD cb; // incremental count of bytes
BYTE *hp; // byte pointer
DWORD dwTmp;
pbih = (PBITMAPINFOHEADER) pbi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
if (!lpBits)
//errhandler("GlobalAlloc", hwnd);
cout << "!lpBits" << endl;
// Retrieve the color table (RGBQUAD array) and the bits
// (array of palette indices) from the DIB.
if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi,
DIB_RGB_COLORS))
{
//errhandler("GetDIBits", hwnd);
cout << "Error 1" << endl;
}
// Create the .BMP file.
hf = CreateFile(pszFile,
GENERIC_READ | GENERIC_WRITE,
(DWORD) 0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);
if (hf == INVALID_HANDLE_VALUE)
//errhandler("CreateFile", hwnd);
hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
// Compute the size of the entire file.
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
// Compute the offset to the array of color indices.
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof (RGBQUAD);
// Copy the BITMAPFILEHEADER into the .BMP file.
if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
(LPDWORD) &dwTmp, NULL))
{
//errhandler("WriteFile", hwnd);
cout << "!WriteFile" << endl;
}
// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)
+ pbih->biClrUsed * sizeof (RGBQUAD),
(LPDWORD) &dwTmp, ( NULL)))
//errhandler("WriteFile", hwnd);
cout << "!WriteFile" << endl;
// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))
//errhandler("WriteFile", hwnd);
cout << "if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))" << endl;
// Close the .BMP file.
if (!CloseHandle(hf))
//errhandler("CloseHandle", hwnd);
// Free memory.
GlobalFree((HGLOBAL)lpBits);
}
int main() {
cout << "ScreenShot - Takes a screen shot\nScreen shot will be put in your clipboard"
"\nThere will be 10 seconds before it takes the screen shot\n" << endl;
string input;
do
{
cin >> input;
if(input == "ScreenShot")
{
/*for(int i=1; i<11; i++)
{
Sleep(1000);
cout << i << endl;
if(i == 10)
{
break;
}
}*/
GetScreenShot();
PBITMAPINFO pbmi = CreateBitmapInfoStruct(hBitmap);
CreateBMPFile("C:\\Users\\Owner\\Desktop\\BITMAP.bmp", pbmi, hBitmap, hScreen);
cout << "ScreenShot taken!" << endl;
cin.ignore(2);
Sleep(3000);
break;
}
else
{
cout << "Invalid command." << endl;
}
} while(true);
return 0;
}
Passing uninitialized values to GetBitmapDimensionEx and GetBitmapBits doesn't seems good, so delete them since they doesn't seem to be used.
void GetScreenShot(void)
{
int x1, y1, x2, y2, w, h;
// get screen dimensions
x1 = GetSystemMetrics(SM_XVIRTUALSCREEN);
x2 = GetSystemMetrics(SM_CXVIRTUALSCREEN);
y1 = GetSystemMetrics(SM_YVIRTUALSCREEN);
y2 = GetSystemMetrics(SM_CYVIRTUALSCREEN);
w = x2-x1;
h = y2-y1;
// copy screen to bitmap
hScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hScreen);
hBitmap = CreateCompatibleBitmap(hScreen, w, h);
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BOOL bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY);
// save bitmap to clipboard
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap);
CloseClipboard();
// clean up
SelectObject(hDC, old_obj);
/*DeleteDC(hDC);
ReleaseDC(NULL, hScreen);
DeleteObject(hBitmap);*/
}
Then, set hdr.bfType even where hf != INVALID_HANDLE_VALUE.
change
if (hf == INVALID_HANDLE_VALUE)
//errhandler("CreateFile", hwnd);
hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
to
if (hf == INVALID_HANDLE_VALUE)
{
//errhandler("CreateFile", hwnd);
}
hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
(add braces)
Using LPCTSTR instead of LPTSTR for type of pszFile, which is an argument of CreateBMPFile is also good
to avoid compiler warning when a string literal is passed.
I use a function which captures a screen using the BitBlt mehtod and can then return a HBITMAP.
int screenCapture() {
int width = 1000;
int height = 700;
HDC hdcTemp, hdc;
BYTE* bitPointer;
hdc = GetDC(HWND_DESKTOP);
hdcTemp = CreateCompatibleDC(hdc);
BITMAPINFO bitmap;
bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
bitmap.bmiHeader.biWidth = width;
bitmap.bmiHeader.biHeight = -height;
bitmap.bmiHeader.biPlanes = 1;
bitmap.bmiHeader.biBitCount = 24;
bitmap.bmiHeader.biCompression = BI_RGB;
bitmap.bmiHeader.biSizeImage = 0;
bitmap.bmiHeader.biClrUsed = 0;
bitmap.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
SelectObject(hdcTemp, hBitmap);
BitBlt(hdcTemp, 0, 0, width, height, hdc, 0, 0, SRCCOPY);
ReleaseDC(HWND_DESKTOP, hdc);
DeleteDC(hdcTemp);
return (int)bitPointer[0];
}
Here, the function only returns the first value of the pixels array.
Actually, it works fine.
for (int i = 0; i >= 0; i++) {
cout << i << ": " << screenCapture() << endl;
}
But when I try to loop this, an error is generated after a few hundred rounds (a little over 900 for me), an Access violation reading location error.
I also noticed that if I reduced the values of width and height, then the error took longer to be called.
I am a true beginner and I do not know where the error may come, but it would look like a memory problem, right?
As was stated in comments, you are leaking your HBITMAP, and also the original HBITMAP that was already in the HDC before you called SelectObject(). Whenever you use SelectObject(), you must always restore the original value (you don't own it).
And don't forget to do error checking!
Try this:
int screenCapture()
{
int result = -1;
int width = 1000;
int height = 700;
HDC hdcTemp, hdc;
BYTE* bitPointer;
hdc = GetDC(HWND_DESKTOP);
if (hdc != NULL)
{
hdcTemp = CreateCompatibleDC(hdc);
if (hdcTemp != NULL)
{
BITMAPINFO bitmap;
bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
bitmap.bmiHeader.biWidth = width;
bitmap.bmiHeader.biHeight = -height;
bitmap.bmiHeader.biPlanes = 1;
bitmap.bmiHeader.biBitCount = 24;
bitmap.bmiHeader.biCompression = BI_RGB;
bitmap.bmiHeader.biSizeImage = 0;
bitmap.bmiHeader.biClrUsed = 0;
bitmap.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)&bitPointer, NULL, NULL);
if (hBitmap != NULL)
{
HBITMAP hPrevBitmap = SelectObject(hdcTemp, hBitmap);
BitBlt(hdcTemp, 0, 0, width, height, hdc, 0, 0, SRCCOPY);
result = (int) bitPointer[0];
SelectObject(hdcTemp, hPrevBitmap);
DeleteObject(hBitmap);
}
DeleteDC(hdcTemp);
}
ReleaseDC(HWND_DESKTOP, hdc);
}
return result;
}