Link to home
Create AccountLog in
Avatar of urif
urif

asked on

Saving a screenshot to a buffer

using the function posted below I can take a screenshot and save it to a file (in this case a JPEG).
Is there any way to save the data from the screenshot to a buffer (an array of bytes, or chars or whatever) instead of saving it to disk?

Code is appreciated, in C please. Thanks.
int GetScreeny(LPWSTR lpszFilename, ULONG uQuality)
{
	 ULONG_PTR gdiplusToken;
	GdiplusStartupInput gdiplusStartupInput;
	GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
	
	HDC hdcScreen  = CreateDC("DISPLAY", NULL, NULL, NULL);
	HDC hdcCapture = CreateCompatibleDC(hdcScreen);
	int nWidth     = GetDeviceCaps(hdcScreen, HORZRES),
	    nHeight    = GetDeviceCaps(hdcScreen, VERTRES),
	    nBPP       = GetDeviceCaps(hdcScreen, BITSPIXEL);
	
	LPBYTE lpCapture;
	BITMAPINFO bmiCapture = { {
		sizeof(BITMAPINFOHEADER), nWidth, -nHeight, 1, nBPP, BI_RGB, 0, 0, 0, 0, 0,
	} };
	HBITMAP hbmCapture = CreateDIBSection(hdcScreen, &bmiCapture,
		DIB_PAL_COLORS, (LPVOID *)&lpCapture, NULL, 0);
	if(!hbmCapture){
		DeleteDC(hdcCapture);
		DeleteDC(hdcScreen);
		GdiplusShutdown(gdiplusToken);
		return 1;
	}
	
	int nCapture = SaveDC(hdcCapture);
	SelectObject(hdcCapture, hbmCapture);
	BitBlt(hdcCapture, 0, 0, nWidth, nHeight, hdcScreen, 0, 0, SRCCOPY);
	RestoreDC(hdcCapture, nCapture);
	DeleteDC(hdcCapture);
	DeleteDC(hdcScreen);
	
	CLSID imageCLSID;
	Bitmap *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);
	int result = (pScreenShot->Save(lpszFilename, &imageCLSID, &encoderParams) == Ok);
	delete pScreenShot;
	DeleteObject(hbmCapture);
	GdiplusShutdown(gdiplusToken);
	return result;
 
}

Open in new window

Avatar of JohnGaby
JohnGaby
Flag of Afghanistan image

Since you are already using GDI+, you can simply call the LockBits method of the Bitmap class to give you the bitmap in whatever format you desire.

http://msdn.microsoft.com/en-us/library/ms536298(VS.85).aspx
Avatar of urif
urif

ASKER

It seems to be the right function. I'm trying to get all the bitmap's content as a jped and instead of saving it to a file i want to save those bytes into a buffer. Then just dump the buffer to either a flat file or to the stdout.
the GetEncoderClsid() function on the code that i posted earlier is here below.

any idea of the code for doing this? I mean, from the example on the MSDN i can learn a bit about how to read that data but i am still having trouble putting all those bytes into a buffer that i can use.

thanks
int GetEncoderClsid(WCHAR *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;
}

Open in new window

So do you want the jpeg output saved to a memory block?  If that is the case, you should use the Save method which takes a stream (instead of a file name).

http://msdn.microsoft.com/en-us/library/ms535406(VS.85).aspx

To create a memory stream, you can use CreateStreamOnHGlobal:

http://msdn.microsoft.com/en-us/library/ms535406(VS.85).aspx
I messed up the link to the CreateStreamOnHGlobal.  Here is the correct link:

http://msdn.microsoft.com/en-us/library/aa378980.aspx
Avatar of urif

ASKER

Thanks,

I run into those functions after your first post as I was searching the gdiplus API.
Maybe it is my lack of experience with COM, or the fact that i never got any of the MSDN code to work or the fact that I don't understand the way the MSDN explains anything (i'm an old time UNIX programmer).
In any case, i still can't find a way to save that image to a buffer.
If you can, could you please post some working code that can do this?

I appreciate the help
Come now, it is not that hard.  (I am a UNIX programmer from way back myself).  Attached is a code snippet which shows you how you might do it.
HGLOBAL	hMem;
 
if (hMem = GlobalAlloc(GMEM_MOVEABLE, 0))
{
    IStream * pStream;
 
    if (CreateStreamOnHGlobal(hMem, FALSE, &pStream) == S_OK)
    {
        Status result;
 
        result = pBitmap->Save(pStream, pEncoder);
 
        pStream->Release();
 
        if (result == Ok)
        {
            unsigned char * pBytes;
 
            if (pBytes = (unsigned char *) GlobalLock(hMem))
            {
                /*
                 * pBytes now points to your image file in memory
                 *  in whatever format was specified by your encoder
                 *	  parameter.
                 */
            }
        }
    }
}

Open in new window

Avatar of urif

ASKER

error C2664: 'Gdiplus::Status Gdiplus::Image::Save(const WCHAR *,const CLSID *,const Gdiplus::EncoderParameters *)' : cannot convert parameter 1 from 'IStream *' to 'const WCHAR *'
1>        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

I am casting it but i still get the error. besides, you are placing inside the save something i assume is EncoderParameters  (the variable is pEncoder). Now, maybe i am missing something but that won't point to the actual bitmap data.
anyway, it's a mess to try to putt hat code inside the original function since i am guessing most of the variables on your code.
Avatar of urif

ASKER

ok, this code below is my try. it compiles but it just does nothing. I debug it but nothing gets saved anywhere.

	if (hMem = GlobalAlloc(GMEM_MOVEABLE, 0))
	{
		IStream * pStream;
	 
		if (CreateStreamOnHGlobal(hMem, FALSE, &pStream) == S_OK)
		{
			
			// 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);
	 
			result = pScreenShot->Save(pStream, &imageCLSID, &encoderParams);
	 
			pStream->Release();
	 
			//if (result == Ok)
			//{
				unsigned char * pBytes;
	 
				if (pBytes = (unsigned char *) GlobalLock(hMem))
				{
					/*
					 * pBytes now points to your image file in memory
					 *  in whatever format was specified by your encoder
					 *        parameter.
					 */
					printf("bitmap = %s\n", pBytes);
				}
				else
				{
					printf("error GlobalLock. Last err: %d\n", GetLastError());
					return 0;
				}
			//}
 
		}
		else
		{
			printf("error CreateStreamOnHGlobal. Last err: %d\n", GetLastError());
			return 0;
		}
	}
	else
	{
		printf("error GlobalAlloc. Last err: %d\n", GetLastError());
		return 0;
	}
 
	return result;

Open in new window

Is the save returning OK (I notice that you have the check commented out)?  I am not really sure why you are trying to print the string returned by GlobalLock.  It is NOT an ASCII string that you can print in that manner, rather it is a pointer to the binary JPEG file.

Perhaps if you tell me exactly what you are trying to do, I might be able to suggest something.
Avatar of urif

ASKER

the printf was there for the purpose of testing.
what i need to do is to grab that jpeg data, copy it to an array to whatever and wither save it to a file or add it to another buffer. That's all.
thanks for the help
ASKER CERTIFIED SOLUTION
Avatar of JohnGaby
JohnGaby
Flag of Afghanistan image

Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
See answer
Avatar of urif

ASKER

great! thanks!
Avatar of urif

ASKER

thanks so much for all the effort.