[Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 646
  • Last Modified:

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

0
urif
Asked:
urif
  • 7
  • 6
1 Solution
 
JohnGabyCommented:
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
0
 
urifAuthor Commented:
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

0
 
JohnGabyCommented:
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
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
JohnGabyCommented:
I messed up the link to the CreateStreamOnHGlobal.  Here is the correct link:

http://msdn.microsoft.com/en-us/library/aa378980.aspx
0
 
urifAuthor Commented:
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
0
 
JohnGabyCommented:
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

0
 
urifAuthor Commented:
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.
0
 
urifAuthor Commented:
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

0
 
JohnGabyCommented:
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.
0
 
urifAuthor Commented:
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
0
 
JohnGabyCommented:
Here is a complete, working program which captures the screen, writes the image to a memory block in JPEG format, then writes that block to a file.
// ScreenCapture.cpp : Defines the entry point for the console application.
//
 
#include "stdafx.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
}
 
void ScreenShot(void)
{
    HGLOBAL hMem;
    HDC hdcScreen  = CreateDC(_T("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);
	    return;
    }
	
    int nCapture = SaveDC(hdcCapture);
    SelectObject(hdcCapture, hbmCapture);
    BitBlt(hdcCapture, 0, 0, nWidth, nHeight, hdcScreen, 0, 0, SRCCOPY);
    RestoreDC(hdcCapture, nCapture);
    DeleteDC(hdcCapture);
    DeleteDC(hdcScreen);
 
	if (hMem = GlobalAlloc(GMEM_MOVEABLE, 0))
	{
		IStream * pStream;
	 
		if (CreateStreamOnHGlobal(hMem, FALSE, &pStream) == S_OK)
		{
			// save the buffer to a file	
			Bitmap * pScreenShot = new Bitmap(hbmCapture, (HPALETTE)NULL);
            CLSID   imageCLSID;
            Status result;
 
			GetEncoderClsid(L"image/jpeg", &imageCLSID);
	 
			result = pScreenShot->Save(pStream, &imageCLSID);
	 
			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.
					 */
                    DWORD   size    = GlobalSize(hMem);
                    FILE    *   fp;
 
                    if (fp = _tfopen(_T("c:\\test.jpg"), _T("wb")))
                    {
                        fwrite(pBytes, size, 1, fp);
                        fclose(fp);
                    }
				}
			}
 
		}
		else
		{
			printf("error CreateStreamOnHGlobal. Last err: %d\n", GetLastError());
			return;
		}
	}
	else
	{
		printf("error GlobalAlloc. Last err: %d\n", GetLastError());
		return;
	}
}
 
 
int _tmain(int argc, _TCHAR* argv[])
{
    ULONG_PTR gdiplusToken;
	GdiplusStartupInput gdiplusStartupInput;
	GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
 
    ScreenShot();
 
    return 0;
}

Open in new window

0
 
urifAuthor Commented:
great! thanks!
0
 
urifAuthor Commented:
thanks so much for all the effort.
0

Featured Post

Efficient way to get backups off site to Azure

This user guide provides instructions on how to deploy and configure both a StoneFly Scale Out NAS Enterprise Cloud Drive virtual machine and Veeam Cloud Connect in the Microsoft Azure Cloud.

  • 7
  • 6
Tackle projects and never again get stuck behind a technical roadblock.
Join Now