Solved

Saving a screenshot to a buffer

Posted on 2009-05-05
13
606 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
  • 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
 
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
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 

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

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

Suggested Solutions

As more and more people are shifting to the latest .Net frameworks, the windows presentation framework is gaining importance by the day. Many people are now turning to WPF controls to provide a rich user experience. I have been using WPF controls fo…
For a while now I'v been searching for a circular progress control, much like the one you get when first starting your Silverlight application. I found a couple that were written in WPF and there were a few written in Silverlight, but all appeared o…
The goal of this video is to provide viewers with basic examples to understand opening and writing to files in the C programming language.
Video by: Grant
The goal of this video is to provide viewers with basic examples to understand and use while-loops in the C programming language.

757 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

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now