I have taken a screenshot programtically and I want to write out the BGRA to file. The BGRA is held in pixelBuffer.
Here is the code I am using to write to file:
BYTE *pixelBuffer;
HBITMAP hbmp;
hbmp = CreateDIBSection(hdcScreen, &bmi, DIB_RGB_COLORS, (void **)&pixelBuffer, NULL, 0);
FILE *stream;\
if (fopen_s(&stream, "C:\\Users\Vayeate\\Desktop\\blah.txt", "wb+") == 0) {
fwrite(pixelBuffer, (screenHeight * screenWidth * 4), (size_t)(sizeof(CHAR) + 1), stream); // (screenWidth * screenHeight * 4)
fclose(stream);
}
However this writes a bunch of gibberish that looks like:
òîîÿòîîÿ
And that repeat forever. I was hoping to get something like 255, 100, 100, 255.
Here is my full code:
#include <Windows.h>
#include <stdio.h>
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE, LPSTR, int)
{
//Sleep(1000)
MessageBox(0, L"Hello World", L"Unipen", MB_ICONINFORMATION);
int i = 0;
DISPLAY_DEVICE device;
device.cb = sizeof(device);
while (EnumDisplayDevices(NULL, i, &device, 0) && ++i) {
if ((device.StateFlags & DISPLAY_DEVICE_ACTIVE) != DISPLAY_DEVICE_ACTIVE) {
MessageBox(0, device.DeviceName, L"CONTINUE", MB_ICONINFORMATION);
continue;
}
MessageBox(0, device.DeviceName, L"BREAK", MB_ICONINFORMATION);
break;
}
size_t screenWidth = 1920;
size_t screenHeight = 1200;
size_t colorLen = 4;
HDC hdcScreen;
hdcScreen = CreateDC(NULL, device.DeviceName, NULL, NULL);
if (hdcScreen == (HDC)NULL) {
MessageBox(0, L"UnableToCreateDC", L"ERROR", MB_ICONINFORMATION);
return 0;
}
HDC hdcMemoryDC;
hdcMemoryDC = CreateCompatibleDC(hdcScreen);
if (hdcMemoryDC == (HDC)NULL) {
DeleteDC(hdcScreen);
MessageBox(0, L"UnableToCreateCompatibleDC", L"ERROR", MB_ICONINFORMATION);
return 0;
}
BITMAPINFO bmi;
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = (LONG)screenWidth;
bmi.bmiHeader.biHeight = (-1)*(LONG)screenHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
BYTE *pixelBuffer;
HBITMAP hbmp;
hbmp = CreateDIBSection(hdcScreen, &bmi, DIB_RGB_COLORS, (void **)&pixelBuffer, NULL, 0);
if (hbmp == (HBITMAP)NULL) {
DeleteDC(hdcScreen);
DeleteDC(hdcMemoryDC);
MessageBox(0, L"UnableToCreateDIBSection", L"ERROR", MB_ICONINFORMATION);
return 0;
}
//HGDIOBJ rez_selected;
HBITMAP rez_selected_bmp;
rez_selected_bmp = (HBITMAP)SelectObject(hdcMemoryDC, hbmp);
if (rez_selected_bmp == (HBITMAP)NULL) {
DeleteDC(hdcScreen);
DeleteDC(hdcMemoryDC);
DeleteObject(hbmp);
MessageBox(0, L"UnableToCreateDIBSection", L"ERROR", MB_ICONINFORMATION);
return 0;
}
BitBlt(hdcMemoryDC, 0, 0, screenWidth, screenHeight, hdcScreen, 0, 0, SRCCOPY);
//(void) SelectObject(hdcMemoryDC, rez_selected_bmp); // i dont do this step in nativeshot
FILE *stream;\
if (fopen_s(&stream, "C:\\Users\Vayeate\\Desktop\\blah.txt", "wb+") == 0) {
fwrite("asdfasdfasdf", 1, (size_t)(sizeof(CHAR) + 1), stream); // (screenWidth * screenHeight * 4)
fclose(stream);
}
DeleteDC(hdcScreen);
DeleteDC(hdcMemoryDC);
DeleteObject(hbmp);
MessageBox(0, L"DONE", L"Unipen", MB_ICONINFORMATION);
return 0;
}
How can I get space delimited BGRA data to file?
You're writing binary data and reading it as text. Thus, òîîÿòîîÿ is incidentally probably the correct output, as these are the characters that the binary values map to, according to the character map used by the text editor that displayed it.
If you want to have space-delimited values of the pixels in the buffer as "human-readable" ASCII written into a text file, you'll first have to use fprintf to convert these values accordingly.
Use this as an example :
if (fopen_s(&stream, "C:\\Users\Vayeate\\Desktop\\blah.txt", "w") == 0) {
for(size_t px = 0 ; px < (screenHeight * screenWidth * 4) ; ++px) {
fprintf(stream, "%hhu, ", pixelBuffer[px]);
}
fclose(stream);
}
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I tried to follow the example found:
Capturing an image from a minimized window
My code:
#include <iostream>
#include <string>
#include <windows.h>
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")
#pragma warning(disable : 4996)
using namespace std;
using namespace Gdiplus;
int GetEncoderClsid(LPCWSTR format, CLSID* pClsid)
{
unsigned int num = 0, size = 0;
GetImageEncodersSize(&num, &size);
if (size == 0) return -1;
ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL) return -1;
GetImageEncoders(num, size, pImageCodecInfo);
for (unsigned int j = 0; j < num; ++j) {
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j;
}
}
free(pImageCodecInfo);
return -1;
}
int SaveScreenshot(string filename, ULONG uQuality, HWND hwnd) // by Napalm
{
ULONG_PTR gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HWND hMyWnd = hwnd;
//HWND hMyWnd = GetDesktopWindow();
RECT r;
int w, h;
HDC dc, hdcCapture;
int nBPP, nCapture, iRes;
LPBYTE lpCapture;
CLSID imageCLSID;
Bitmap* pScreenShot;
// get the area of my application's window
GetWindowRect(hMyWnd, &r);
dc = GetWindowDC(hMyWnd); // GetDC(hMyWnd) ;
w = r.right - r.left;
h = r.bottom - r.top;
nBPP = GetDeviceCaps(dc, BITSPIXEL);
hdcCapture = CreateCompatibleDC(dc);
// create the buffer for the screenshot
BITMAPINFO bmiCapture = { sizeof(BITMAPINFOHEADER), w, -h, 1, nBPP, BI_RGB, 0, 0, 0, 0, 0, };
// create a container and take the screenshot
HBITMAP hbmCapture = CreateDIBSection(dc, &bmiCapture, DIB_PAL_COLORS, (LPVOID*)&lpCapture, NULL, 0);
// failed to take it
if (!hbmCapture) {
DeleteDC(hdcCapture);
DeleteDC(dc);
GdiplusShutdown(gdiplusToken);
printf("failed to take the screenshot. err: %d\n", GetLastError());
return 0;
}
// copy the screenshot buffer
nCapture = SaveDC(hdcCapture);
SelectObject(hdcCapture, hbmCapture);
BitBlt(hdcCapture, 0, 0, w, h, dc, 0, 0, SRCCOPY);
RestoreDC(hdcCapture, nCapture);
DeleteDC(hdcCapture);
DeleteDC(dc);
// save the buffer to a file
pScreenShot = new Bitmap(hbmCapture, (HPALETTE)NULL);
EncoderParameters encoderParams;
encoderParams.Count = 1;
encoderParams.Parameter[0].NumberOfValues = 1;
encoderParams.Parameter[0].Guid = EncoderQuality;
encoderParams.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParams.Parameter[0].Value = &uQuality;
GetEncoderClsid(L"image/jpeg", &imageCLSID);
wchar_t* lpszFilename = new wchar_t[filename.length() + 1];
mbstowcs(lpszFilename, filename.c_str(), filename.length() + 1);
iRes = (pScreenShot->Save(lpszFilename, &imageCLSID, &encoderParams) == Ok);
delete pScreenShot;
DeleteObject(hbmCapture);
GdiplusShutdown(gdiplusToken);
return iRes;
}
int main() {
HWND hWnd;
hWnd = FindWindowA(NULL, "txt.txt - Bloco de Notas");
WINDOWPLACEMENT wp = { 0 };
wp.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(hWnd, &wp);
ANIMATIONINFO ai = { 0 };
bool restoreAnimated = false;
if (wp.showCmd == SW_SHOWMINIMIZED)
{
ai.cbSize = sizeof(ANIMATIONINFO);
SystemParametersInfo(SPI_GETANIMATION, sizeof(ANIMATIONINFO), &ai, 0);
if (ai.iMinAnimate != 0)
{
ai.iMinAnimate = 0;
SystemParametersInfo(SPI_SETANIMATION, sizeof(ANIMATIONINFO), &ai, 0);
restoreAnimated = true;
}
// optionally move the window off-screen, or
// apply alpha using SetLayeredWindowAttributes()...
ShowWindow(hWnd, SW_SHOWNOACTIVATE);
}
// capture as needed ...
string path = "C:\\Users\\CAIO\\Desktop\\screenshot.jpg";
ULONG quality = 100;
SaveScreenshot(path, quality, hWnd);
if (wp.showCmd == SW_SHOWMINIMIZED)
{
SetWindowPlacement(hWnd, &wp);
// optionally remove alpha using SetLayeredWindowAttributes()...
if (restoreAnimated)
{
ai.iMinAnimate = 1;
SystemParametersInfo(SPI_SETANIMATION, sizeof(ANIMATIONINFO), &ai, 0);
}
}
return 0;
}
In her code there's restoreAnimation = true; and if (restoreAnimation) did he mean restoreAnimated?
With the code above it still shown the window for a sec, also the captured image is all black (maybe captured while minimized) or is not captured properly.
https://youtu.be/8b1wXxtaXsY?t=9
Sec 8 and 9 from the video you can see the window is being shown on the screen.
Suggestions?
did he mean restoreAnimated?
I think yes.
This code from #Remy only disables the animation effects when minimizing and restoring, the window will still be displayed for a short time when restoring. As the answer pointed out, you can use SetLayeredWindowAttributes to make the window nearly completely transparent(It should be noted that using SetLayeredWindowAttributes needs to ensure that hwnd has WS_EX_LAYERED style.) and then you will not see the window, but the operating system will.
In addition, the problem of incomplete window capture is because the window is not drawn completely, call UpdateWindow after ShowWindow works for me, it will send a WM_PAINT message to the window.
bool restoreAnimated = false;
BYTE Alph = 0;
LONG_PTR exstyle = 0;
if (wp.showCmd == SW_SHOWMINIMIZED)
{
ai.cbSize = sizeof(ANIMATIONINFO);
SystemParametersInfo(SPI_GETANIMATION, sizeof(ANIMATIONINFO), &ai, 0);
if (ai.iMinAnimate != 0)
{
ai.iMinAnimate = 0;
SystemParametersInfo(SPI_SETANIMATION, sizeof(ANIMATIONINFO), &ai, 0);
restoreAnimated = true;
}
// optionally move the window off-screen, or
// apply alpha using SetLayeredWindowAttributes()...
exstyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE);
SetWindowLongPtr(hWnd, GWL_EXSTYLE, exstyle | WS_EX_LAYERED);
DWORD flag;
BOOL ret = GetLayeredWindowAttributes(hWnd, 0, &Alph, 0);
SetLayeredWindowAttributes(hWnd, 0, 1, LWA_ALPHA);
ShowWindow(hWnd, SW_SHOWNOACTIVATE);
UpdateWindow(hWnd);
}
// capture as needed ...
string path = "C:\\Users\\name\\Desktop\\screenshot.jpg";
ULONG quality = 100;
SaveScreenshot(path, quality, hWnd);
if (wp.showCmd == SW_SHOWMINIMIZED)
{
SetWindowPlacement(hWnd, &wp);
// optionally remove alpha using SetLayeredWindowAttributes()...
SetLayeredWindowAttributes(hWnd, 0, Alph, LWA_ALPHA);
SetWindowLongPtr(hWnd, GWL_EXSTYLE, exstyle);
if (restoreAnimated)
{
ai.iMinAnimate = 1;
SystemParametersInfo(SPI_SETANIMATION, sizeof(ANIMATIONINFO), &ai, 0);
}
}
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 am using Visual Studio 2017, I wrote code which should create a folder, capture a screenshot when the mouse button is pressed and save the screenshot to a .bmp file. But I don't know why that script does not work. Visual Studio compile it without errors / warnings.
Here is the code:
// variable to store the HANDLE to the hook. Don't declare it anywhere else then globally
// or you will get problems since every function uses this variable.
HHOOK _hook;
// This struct contains the data received by the hook callback. As you see in the callback function
// it contains the thing you will need: vkCode = virtual key code.
KBDLLHOOKSTRUCT kbdStruct;
int filenum = 1;
// This is the callback function. Consider it the event that is raised when, in this case,
// a key is pressed.
void TakeScreenShot(const char* filename)
{
//keybd_event(VK_SNAPSHOT, 0x45, KEYEVENTF_EXTENDEDKEY, 0);
//keybd_event(VK_SNAPSHOT, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
HBITMAP h;
POINT a, b;
a.x = 0;
a.y = 0;
b.x = GetSystemMetrics(SM_CXSCREEN);
b.y = GetSystemMetrics(SM_CYSCREEN);
HDC hScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, abs(b.x - a.x), abs(b.y - a.y));
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BOOL bRet = BitBlt(hDC, 0, 0, abs(b.x - a.x), abs(b.y - a.y), hScreen, a.x, a.y, 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);
OpenClipboard(NULL);
h = (HBITMAP)GetClipboardData(CF_BITMAP);
CloseClipboard();
HDC hdc = NULL;
FILE*fp = NULL;
LPVOID pBuf = NULL;
BITMAPINFO bmpInfo;
BITMAPFILEHEADER bmpFileHeader;
do
{
hdc = GetDC(NULL);
ZeroMemory(&bmpInfo, sizeof(BITMAPINFO));
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
GetDIBits(hdc, h, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS);
if (bmpInfo.bmiHeader.biSizeImage <= 0)
bmpInfo.bmiHeader.biSizeImage = bmpInfo.bmiHeader.biWidth*abs(bmpInfo.bmiHeader.biHeight)*(bmpInfo.bmiHeader.biBitCount + 7) / 8;
if ((pBuf = malloc(bmpInfo.bmiHeader.biSizeImage)) == NULL)
{
MessageBox(NULL, TEXT("Unable to Allocate Bitmap Memory"), TEXT("Error"), MB_OK | MB_ICONERROR);
break;
}
bmpInfo.bmiHeader.biCompression = BI_RGB;
GetDIBits(hdc, h, 0, bmpInfo.bmiHeader.biHeight, pBuf, &bmpInfo, DIB_RGB_COLORS);
if ((fp = fopen(filename, "wb")) == NULL)
{
MessageBox(NULL, TEXT("Unable to Create Bitmap File"), TEXT("Error"), MB_OK | MB_ICONERROR);
break;
}
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + bmpInfo.bmiHeader.biSizeImage;
bmpFileHeader.bfType = 'MB';
bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
fwrite(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, fp);
fwrite(&bmpInfo.bmiHeader, sizeof(BITMAPINFOHEADER), 1, fp);
fwrite(pBuf, bmpInfo.bmiHeader.biSizeImage, 1, fp);
}
while (false);
if (hdc)ReleaseDC(NULL, hdc);
if (pBuf) free(pBuf);
if (fp)fclose(fp);
}
LRESULT __stdcall HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode >= 0)
{
// the action is valid: HC_ACTION.
if (wParam == WM_LBUTTONDOWN)
{
std::string OutputFolder = "C:\\temp";
std::string filename = "ss";
if (CreateDirectory(OutputFolder.c_str(), NULL) ||
ERROR_ALREADY_EXISTS == GetLastError())
{
}
else
{
// Failed to create directory.
}
auto numfile = std::to_string(filenum);
TakeScreenShot((OutputFolder + "\\" + filename + std::to_string(filenum) + ".bmp").c_str());
filenum++;
}
}
// call the next hook in the hook chain. This is nessecary or your hook chain will break and the hook stops
return CallNextHookEx(_hook, nCode, wParam, lParam);
}
void ReleaseHook()
{
UnhookWindowsHookEx(_hook);
}
int main()
{
// Don't mind this, it is a meaningless loop to keep a console application running.
// I used this to test the keyboard hook functionality. If you want to test it, keep it in ;)
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
}
}
If click with it does not make the directory (if it does not exist) and does not create the .bmp file.
As others have said, you never install your hook! Also, it appears from my tests that you need to be dispatching messages to a window of some sort in order for a WH_MOUSE hook to get called.
Here's a minimal version of main () that works for me:
int main()
{
_hook = SetWindowsHookEx (WH_MOUSE, HookCallback, NULL, GetCurrentThreadId ());
if (_hook == NULL)
...
MessageBox (NULL, "Click OK to quit", "Screen Grabber", MB_OK);
UnhookWindowsHookEx (_hook);
}
Then the rest of your code works fine, albeit is a little bit messy as others have said.
However, this will only catch mouse-clicks within the message box itself, and I don't think that's what you want.
If you want to catch these globally, you will need to install a "low level" mouse hook. This needs to be a global hook but otherwise the code looks much the same. The code to install and run the hook is:
int main()
{
_hook = SetWindowsHookEx (WH_MOUSE_LL, HookCallback, NULL, 0);
MSG msg;
while (GetMessage (&msg, NULL, 0, 0))
DispatchMessage (&msg);
UnhookWindowsHookEx(_hook);
}
This question already has answers here:
Save HBITMAP to *.bmp file using only Win32
(5 answers)
Closed 8 years ago.
Ok, whole story is, I am trying to use Leptonica+Tesseract OCR in C++ to take a screenshot, save it to a *.bmp file, then load it back up to OCR with it. I won't need to do this frequently, but as I cannot seem to copy the screenshot data directly into a Leptonica PIX structure, I need to save it to a file first..actually a solution to this would be preferably.
Here's some code I've found online, trying to help me out.
Screen cap:
HBITMAP ScreenCapture(){
int width=100;
int height=100;
// get the device context of the screen
HDC hScreenDC = CreateDC(L"DISPLAY", NULL, NULL, NULL);
// and a device context to put it in
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int x = GetDeviceCaps(hScreenDC, HORZRES);
int y = GetDeviceCaps(hScreenDC, VERTRES);
// maybe worth checking these are positive values
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, x, y);
// get a new bitmap
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);
BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);
//GlobalAlloc(GPTR, hBitmap)
WriteDIB(L"test.bmp", (HGLOBAL)hBitmap);
// clean up
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
return hBitmap;
// now your image is held in hBitmap. You can save it or do whatever with it
}
Attempt to write function:
BOOL WriteDIB( LPTSTR szFile, HANDLE hDIB)
{
cout<<endl<<"Running save function";
/*HANDLE hDIB=GlobalAlloc(GPTR, sizeof(hDIBtochange));//this doesn't work, the result is four. Also the HANDLE parameter's name would be changed to hDIBtochange, so that the rest of the function uses the old 'hDIB' throughout
cout<<endl<<sizeof(hDIBtochange);*/
BITMAPFILEHEADER hdr;
LPBITMAPINFOHEADER lpbi;
if (!hDIB)
return FALSE;
CFile file;
if( !file.Open( szFile, CFile::modeWrite|CFile::modeCreate) )
return FALSE;
lpbi = (LPBITMAPINFOHEADER)hDIB;
int nColors = 1 << lpbi->biBitCount;
// Fill in the fields of the file header
hdr.bfType = ((WORD) ('M' << 8) | 'B'); // is always "BM"
hdr.bfSize = GlobalSize (hDIB) + sizeof( hdr );
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
hdr.bfOffBits = (DWORD) (sizeof( hdr ) + lpbi->biSize + nColors * sizeof(RGBQUAD));
// Write the file header
file.Write( &hdr, sizeof(hdr) );
// Write the DIB header and the bits
file.Write( lpbi, GlobalSize(hDIB) );
return TRUE;
}
Shamelessly copied from people's posts over the years.
Ok! Problem I face is, I cannot seem to understand how to GlobalAlloc the Hbitmap into a globally accessible Handle, that can be converted or use with LPBITMAPINFOHEADER.
Soon as lpbi is created, every single field inside of it is "Unable to read memory" error in Visual Studio 2012 debugging. It's inaccessible, despite being created.
Solutions..
Go straight from screencap to PIX, inside of memory..
Find a way to save to bitmap and create them periodically to read..
Find another way entirely that makes more sense..
Preferring first, but, I'm asking for a solution in this, to the second one..or third.
If you need more info I can try to provide it. This mostly boils down to "I've never done code like this before and it wasn't taught in my classes so I'm trying to learn as I go".
A much easier way to save an HBITMAP to file is to make use of GDI+.
This gives you the advantage of being able to save to any format that windows supports natively, while freeing you from the muck of playing around with or even needing to understand, various image formats.
In the below example, I've just used LoadImage as a quik and dirty way of loading a pre-existing image - you could simply use the HBITMAP you've already captured.
Here's an example that loads a bitmap and saves it again. (I had initially used "image/png" as the output type, along with an appropriate output filename)
#include <windows.h>
#include <gdiplus.h>
using namespace Gdiplus;
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 -1; // Failure
}
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HBITMAP hBitmap = (HBITMAP)LoadImage(GetModuleHandle(NULL), "babe.bmp", IMAGE_BITMAP, 0,0, LR_LOADFROMFILE);
Bitmap *image = new Bitmap(hBitmap, NULL);
CLSID myClsId;
int retVal = GetEncoderClsid(L"image/bmp", &myClsId);
image->Save(L"output.bmp", &myClsId, NULL);
delete image;
GdiplusShutdown(gdiplusToken);
return 0;
}
I recently had to do the same thing you are doing and successfully used GlobalAlloc.
The basis of this code is from This MSDN Article.
It looks like you Got your example code from here.
MSDN is really reliable for win32 operations, definitely prefer it over other sites in my experaince.
What seems to be happening is that the sizeof(hDIBtochange) is returning 4, so you are only allocating 4 bytes of memory. which would not be enough to hold a pbi structure.
Here is my code with a GlobalAlloc which hopefully will show the correct usage.
void
WriteBmpTofile(const bool remote, LPSTR pszFile, PBITMAPINFO pbi, HBITMAP hBmp, HDC hDC)
{
HANDLE hFile;
BITMAPFILEHEADER hdr;
PBITMAPINFOHEADER pbih;
LPBYTE lpBits;
DWORD dwTemp;
pbih = (PBITMAPINFOHEADER)pbi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
if(!lpBits)
{
return; // could not allocate bitmap
}
GetDIBits(hDC, hBmp, 0, (WORD)pbih->biHeight, lpBits, pbi, DIB_RGB_COLORS);
hFile = CreateFile(pszFile,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
return; // Could not open screenshot file
}
// type == BM
hdr.bfType = 0x4d42;
hdr.bfSize = (sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
hdr.bfOffBits = sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD);
// write the bitmap file header to file
WriteFile(hFile, (LPVOID)&hdr, sizeof(BITMAPFILEHEADER), &dwTemp, NULL);
// write the bitmap header to file
WriteFile(hFile, (LPVOID)pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof(RGBQUAD), &dwTemp, NULL);
// copy the bitmap colour data into the file
WriteFile(hFile, (LPSTR)lpBits, pbih->biSizeImage, &dwTemp, NULL);
CloseHandle(hFile);
GlobalFree((HGLOBAL)lpBits);
}
Here is the top function in that MSDN article, if you need it (again modified by me).
PBITMAPINFO
Print::CreateBitmapInfo(HBITMAP hBmp)
{
BITMAP bmp;
PBITMAPINFO pbmi;
GetObject(hBmp, sizeof(BITMAP), &bmp);
pbmi = static_cast<PBITMAPINFO>(LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER)));
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes; // we are assuming that there is only one plane
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
// no compression this is an rgb bitmap
pbmi->bmiHeader.biCompression = BI_RGB;
// calculate size and align to a DWORD (8bit), we are assuming there is only one plane.
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * bmp.bmBitsPixel +31) & -31) * pbmi->bmiHeader.biHeight;
// all device colours are important
pbmi->bmiHeader.biClrImportant = 0;
return pbmi;
}
I'm guessing you got your code from here Storing an Image. A while back I had to modify the code to work with WinCE 5.0 and WinCE 6.0. Here is the beta-sample though it is kinda messy. It does it without the GlobalAlloc. It uses CreateDibSection instead.
int CreateBMPFile(HWND hwnd, LPCTSTR 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;
int ret = 0;
pbi = CreateBitmapInfoStruct(NULL, hBMP);
if(pbi == NULL)
{
return ret;
}
pbih = (PBITMAPINFOHEADER) pbi;
/*
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
if (!lpBits)
{
//errhandler("GlobalAlloc", hwnd);
return;
}
*/
RGBQUAD *rgbq;
rgbq = pbi->bmiColors;
PALETTEENTRY pe[256];
GetSystemPaletteEntries(hDC, 0, pbih->biClrUsed, pe);
for(DWORD i = 0; i < pbih->biClrUsed; i++)
{
rgbq[i].rgbRed = pe[i].peRed;
rgbq[i].rgbBlue = pe[i].peBlue;
rgbq[i].rgbGreen = pe[i].peGreen;
rgbq[i].rgbReserved = 0;
}
// CE5.0 + CE6.0
HDC tHDC;
tHDC = CreateCompatibleDC(hDC);
HBITMAP h = CreateDIBSection(hDC, pbi, DIB_PAL_COLORS, (void **)&hp, NULL, 0);
if(h == NULL)
{
goto close_bmp;
}
SelectObject(tHDC, h);
BitBlt(tHDC, 0, 0, SCREEN_W, SCREEN_H, hDC, 0, 0, SRCCOPY);
/*
// 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);
return;
}
*/
// 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);
goto close_bmp;
}
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);
goto close_bmp;
}
// 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);
}
// 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);
goto close_bmp;
}
close_bmp:
// Close the .BMP file.
if(hf != INVALID_HANDLE_VALUE)
{
if (!CloseHandle(hf))
{
//errhandler("CloseHandle", hwnd);
}
else
{
ret = 1;
}
}
// Free memory.
// GlobalFree((HGLOBAL)lpBits);
if(tHDC != NULL)
DeleteObject(tHDC);
if(h != NULL)
DeleteObject(h);
if(pbi != NULL)
{
//LocalFree(pbi);
free(pbi);
}
return ret;
}
I write this simple code for saving bitmap to file (*.bmp) from HWND.
#include <stdio.h>
#include <windows.h>
bool captureAndSave(const HWND& hWnd, int nBitCount, const char* szFilePath)
{
if(!szFilePath || !strlen(szFilePath))
{
printf("bad function arguments\n");
return false;
}
//calculate the number of color indexes in the color table
int nColorTableEntries = -1;
switch(nBitCount)
{
case 1:
nColorTableEntries = 2;
break;
case 4:
nColorTableEntries = 16;
break;
case 8:
nColorTableEntries = 256;
break;
case 16:
case 24:
case 32:
nColorTableEntries = 0;
break;
default:
nColorTableEntries = -1;
break;
}
if(nColorTableEntries == -1)
{
printf("bad bits-per-pixel argument\n");
return false;
}
HDC hDC = GetDC(hWnd);
HDC hMemDC = CreateCompatibleDC(hDC);
int nWidth = 0;
int nHeight = 0;
if(hWnd != HWND_DESKTOP)
{
RECT rect;
GetClientRect(hWnd, &rect);
nWidth = rect.right - rect.left;
nHeight = rect.bottom - rect.top;
}
else
{
nWidth = ::GetSystemMetrics(SM_CXSCREEN);
nHeight = ::GetSystemMetrics(SM_CYSCREEN);
}
HBITMAP hBMP = CreateCompatibleBitmap(hDC, nWidth, nHeight);
SelectObject(hMemDC, hBMP);
BitBlt(hMemDC, 0, 0, nWidth, nHeight, hDC, 0, 0, SRCCOPY);
int nStructLength = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorTableEntries;
LPBITMAPINFOHEADER lpBitmapInfoHeader = (LPBITMAPINFOHEADER)new char[nStructLength];
::ZeroMemory(lpBitmapInfoHeader, nStructLength);
lpBitmapInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
lpBitmapInfoHeader->biWidth = nWidth;
lpBitmapInfoHeader->biHeight = nHeight;
lpBitmapInfoHeader->biPlanes = 1;
lpBitmapInfoHeader->biBitCount = nBitCount;
lpBitmapInfoHeader->biCompression = BI_RGB;
lpBitmapInfoHeader->biXPelsPerMeter = 0;
lpBitmapInfoHeader->biYPelsPerMeter = 0;
lpBitmapInfoHeader->biClrUsed = nColorTableEntries;
lpBitmapInfoHeader->biClrImportant = nColorTableEntries;
DWORD dwBytes = ((DWORD) nWidth * nBitCount) / 32;
if(((DWORD) nWidth * nBitCount) % 32) {
dwBytes++;
}
dwBytes *= 4;
DWORD dwSizeImage = dwBytes * nHeight;
lpBitmapInfoHeader->biSizeImage = dwSizeImage;
LPBYTE lpDibBits = 0;
HBITMAP hBitmap = ::CreateDIBSection(hMemDC, (LPBITMAPINFO)lpBitmapInfoHeader, DIB_RGB_COLORS, (void**)&lpDibBits, NULL, 0);
SelectObject(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, nWidth, nHeight, hDC, 0, 0, SRCCOPY);
ReleaseDC(hWnd, hDC);
BITMAPFILEHEADER bmfh;
bmfh.bfType = 0x4d42; // 'BM'
int nHeaderSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorTableEntries;
bmfh.bfSize = 0;
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorTableEntries;
FILE *pFile = 0;
fopen_s(&pFile, szFilePath, "wb");
if(!pFile)
{
::DeleteObject(hBMP);
::DeleteObject(hBitmap);
delete[]lpBitmapInfoHeader;
printf("can not open file\n");
return false;
}
DWORD nColorTableSize = 0;
if (nBitCount != 24)
nColorTableSize = (1 << nBitCount) * sizeof(RGBQUAD);
else
nColorTableSize = 0;
fwrite(&bmfh, sizeof(BITMAPFILEHEADER), 1, pFile);
fwrite(lpBitmapInfoHeader, nHeaderSize,1,pFile);
if(nBitCount < 16)
{
int nBytesWritten = 0;
RGBQUAD *rgbTable = new RGBQUAD[nColorTableEntries * sizeof(RGBQUAD)];
//fill RGBQUAD table and write it in file
for(int i = 0; i < nColorTableEntries; ++i)
{
rgbTable[i].rgbRed = rgbTable[i].rgbGreen = rgbTable[i].rgbBlue = i;
rgbTable[i].rgbReserved = 0;
fwrite(&rgbTable[i], sizeof(RGBQUAD), 1, pFile);
}
delete[]rgbTable;
/*
RGBQUAD rgb;
for (DWORD i = 0; i < nColorTableEntries ; i++)
{
rgb.rgbBlue = rgb.rgbGreen = rgb.rgbRed = (BYTE)(i*(255/(nColorTableEntries-1)));
nBytesWritten = fwrite(&rgb, 1, sizeof(rgb), pFile);
if (nBytesWritten != sizeof(rgb))
{
printf("error while writing rgb header\n");
fclose(pFile);
::DeleteObject(hBMP);
::DeleteObject(hBitmap);
delete[]lpBitmapInfoHeader;
return false;
}
}
*/
}
fwrite(lpDibBits, dwSizeImage, 1, pFile);
fclose(pFile);
::DeleteObject(hBMP);
::DeleteObject(hBitmap);
delete[]lpBitmapInfoHeader;
}
int main(int argc, char **argv)
{
captureAndSave(HWND_DESKTOP, 1, "1.bmp");
captureAndSave(HWND_DESKTOP, 4, "4.bmp");
captureAndSave(HWND_DESKTOP, 8, "8.bmp");
captureAndSave(HWND_DESKTOP, 16, "16.bmp");
captureAndSave(HWND_DESKTOP, 24, "24.bmp");
captureAndSave(HWND_DESKTOP, 32, "32.bmp");
return 0;
}
Images are saved properly for 32, 24 and 16 bits per pixel. But for 8, 4 and 1 bits per pixel image contains only black pixels.
Please tell me what I'm doing wrong.
When GDI copies images to an indexed surface, it needs to map the colors it has on the source to colors available on the destination.
Until, and unless, you create and select a palette into the destination DC, GDI won't know what colors are available, and will map the colors using the default palette, which will define black and white only.
The could be quite a lot involved in this - ideally you'd want to scan the source image, create a map of all the colors used and their freqencies and use that to compute an ideal palette.
Or, just use CreateHalfTonePalette.
In your case, you are blitting onto a DIBSection with the required bit depth, so you need to initialize the DIBSections color-table before performing the blit.
iirc, When blitting to a DIB Section, having the DIBSections color table setup is more important than selecting an HPALETTE into the DC - but you can use the palette created by CreateHalfTonePalette, and extract the resulting color table.
For 8/4/1 bit images, which are indexed images, must write RGBQUAD table to file, because raw bitmap data is not colors, but indexes to RQBQUAD table.