Solved

Saving a screenshot to a buffer

Posted on 2009-05-05
13
618 Views
Last Modified: 2013-12-03
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
Comment
Question by:urif
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 6
13 Comments
 
LVL 9

Expert Comment

by:JohnGaby
ID: 24305816
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
 

Author Comment

by:urif
ID: 24306178
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
 
LVL 9

Expert Comment

by:JohnGaby
ID: 24307240
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
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 9

Expert Comment

by:JohnGaby
ID: 24307252
I messed up the link to the CreateStreamOnHGlobal.  Here is the correct link:

http://msdn.microsoft.com/en-us/library/aa378980.aspx
0
 

Author Comment

by:urif
ID: 24307658
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
 
LVL 9

Expert Comment

by:JohnGaby
ID: 24308757
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
 

Author Comment

by:urif
ID: 24314192
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
 

Author Comment

by:urif
ID: 24314280
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
 
LVL 9

Expert Comment

by:JohnGaby
ID: 24314866
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
 

Author Comment

by:urif
ID: 24315116
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
 
LVL 9

Accepted Solution

by:
JohnGaby earned 125 total points
ID: 24317517
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
 

Author Closing Comment

by:urif
ID: 31578054
great! thanks!
0
 

Author Comment

by:urif
ID: 24317758
thanks so much for all the effort.
0

Featured Post

MS Dynamics Made Instantly Simpler

Make Your Microsoft Dynamics Investment Count  & Drastically Decrease Training Time by Providing Intuitive Step-By-Step WalkThru Tutorials.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This tutorial is posted by Aaron Wojnowski, administrator at SDKExpert.net.  To view more iPhone tutorials, visit www.sdkexpert.net. This is a very simple tutorial on finding the user's current location easily. In this tutorial, you will learn ho…
Examines three attack vectors, specifically, the different types of malware used in malicious attacks, web application attacks, and finally, network based attacks.  Concludes by examining the means of securing and protecting critical systems and inf…
The goal of this video is to provide viewers with basic examples to understand and use pointers in the C programming language.
The goal of this video is to provide viewers with basic examples to understand and use structures in the C programming language.

749 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question